Вы находитесь на странице: 1из 228

MVCSoft Inc.

www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Introduction
The MVCSoft JDO Toolkit is an implementation of the Java Data Objects specification
(JDO) for relational databases. With this product, you can store your Java objects in a
relational database using a Java standard (Java Specification Request 12). In conformance
with this specification, your persistence code will be transparent and Java-centric. This
will make developers on your projects more productive. Because this is a standard, your
code will be portable—not just between operating systems and databases, but even
between JDO vendors. And because the standard is designed and implemented by
persistence experts, your code will be more reliable, scalable, and performant.
The JDO specification is easy to understand, and the MVCSoft implementation is easy to
use. This documentation explains both. There are only a handful of interfaces with which
you need be familiar, and only a handful of concepts for you to understand, before you
can begin to use MVCSoft JDO effectively.
This introductory section will attempt to familiarize you with some of the basic concepts
of JDO. You should not be concerned if you don’t understand them completely. Later,
you will see examples and hear about these concepts in depth. Here, you are being
provided with the bird’s-eye view.
Some Advantages of the MVCSoft JDO Toolkit
There are many advantages to selecting MVCSoft as your JDO vendor. Among them are:
Full Source Code
You get the complete source code to every aspect of the MVCSoft JDO Toolkit, from the
GUI tool to the runtime libraries. This has at least three advantages: (1) you can examine
the behavior of your JDO components at runtime in a debugger; (2) you can have
confidence in the behavior of your application, by inspecting the code if you so desire;
and (3) you can modify the code to support your special needs.
Cost and Licensing
The MVCSoft JDO Toolkit is inexpensive. Competing products are priced 10x or greater
this price—without source code. But more important than price is the absence of runtime
royalties. You license only one copy of the JDO Toolkit per developer. You can then
distribute the MVCSoft runtime binaries royalty free.
Developer Productivity
The MVCSoft JDO Toolkit is designed to maximize developer productivity. It comes
with both a GUI development environment and an XDoclet module for configuring the
metadata for your persistent objects. If you use Ant, you can take advantage of the
MVCSoft Ant tasks for generating metadata, enhancing classes, and creating or removing

1
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

database tables. The entire toolkit is designed to work with your project structure, rather
than making you restructure your project to work with it.
Rich Feature Set
The MVCSoft JDO Toolkit is a complete implementation of the required aspects of the
JDO spec. Furthermore:
♦ It allows for inheritance in normalized relational database tables.
♦ It provides for user-defined runtime load groups that can extend across
relationships.
♦ It provides a read-write transactional cache.
♦ It provides for user-defined functions in the JDO query mechanism.
♦ It provides for user-defined transformations of data and mappings.
♦ It manages large result sets effectively.
♦ It provides an additional JDO-aware SQL-based query language (compatible where
possible with Hibernate’s SQL-based query language)
In addition, it implements almost all the optional features described in that spec,
including:
♦ Both datastore and application identity
♦ Transient transactional operations
♦ Non-transactional reads
♦ Retain values
♦ Optimistic transactions
♦ All collection types in normalized relational tables: ArrayList, HashMap,
Hashtable, LinkedList, TreeMap, TreeSet, Vector, Map, List, and arrays
The JDO API
The Java Data Objects specification (JDO) provides for persistence of Java objects. It is
one of several APIs for Java persistence, along with serialization, EJB 1.1, and EJB 2.0.
Unlike serialization, JDO provides for efficient persistence to a variety of data stores.
Unlike the EJB 1.1 and EJB 2.0 specifications, JDO works transparently with simple Java
objects (including the ability to model inheritance), and provides only those services
directly related to saving and restoring the state of those objects. You can use these
persistent Java objects in many different environments, from standalone command-line
utilities to Swing applications to servlets to EJBs.
The MVCSoft JDO Toolkit is an implementation of this public API. This means that you
can safely use MVCSoft’s product with the knowledge that you can port it to any other

2
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

JDO product with minimal (or no) changes. This is the ultimate security for your
development project.
The programmer who uses JDO for object persistence needs to accomplish the four basic
tasks “CRUD” tasks: creating objects in the database, reading existing objects,
updating objects, and deleting objects.

The JDO API is the same regardless of which vendor’s JDO product you
use. This is one primary advantage of using a product written to a public
specification.

The JDO programmer uses seven interfaces to accomplish these tasks:


♦ javax.jdo.PersistenceManagerFactory
♦ javax.jdo.PersistenceManager
♦ javax.jdo.Transaction
♦ javax.jdo.Query
♦ javax.jdo.Extent
♦ javax.jdo.InstanceCallbacks
♦ javax.jdo.JDOHelper
The central interface is javax.jdo.PersistenceManager. There are methods in the
PersistenceManager interface to make an object persistent, delete it, make it transient,
and so forth. The PersistenceManager instance is also the source for query instances,
extent instances, and the object used for starting and stopping the current transaction.
The programmer gets the PersistenceManager from an instance of
javax.jdo.PersistenceManagerFactory (which is—as the name implies—basically just
a factory for PersistenceManagers). The factory instance is either instantiated using a list
of properties, or—in a managed environment like an application server—from the JNDI
namespace.
The most straightforward usage of JDO is to create, read, modify and delete persistent
instances within a transaction. The transaction is started, committed, and rolled back
using an instance of javax.jdo.Transaction available from the PersistenceManager
instance.
There are three ways to find existing objects. If you have the key for the object, you can
get it directly from the PersistenceManager using the key. You can use the special query
language with an instance of javax.jdo.Query to get objects matching certain criteria. Or
you can iterate all the objects of a particular class usng javax.jdo.Extent.
You can implement the javax.jdo.InstanceCallbacks interface if you need directly to
manage aspects of the persistence of an object. For example, when an object is being
deleted, you can cascade this delete to related objects. Finally, you use the

3
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

javax.jdo.JDOHelper class for “bookkeeping” tasks, such as getting the key associated
with a persistent object, or getting an instance of the PersistenceManagerFactory from a
list of properties.
Changes from JDO 1.0 to JDO 1.01
The JDO 1.01 specification made a few significant changes to JDO 1.0, which can break
existing code. The main changes are:
In a non-managed environment (ie outside of an application server) you can no longer
close a PersistenceManager with an active transaction. This would most likely come
up after an exception had been thrown. Consider using code like this:
try
{
// ...
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log exception here
}
pm.close();
}
Also, outside of a managed environment you should close your
PersistenceManagerFactory instances when they will no longer be used, to give the
runtime an opportunity to release resources. Please note that you should not close them
routinely in the acquire-use-release pattern used by resources themselves (e.g. a
PersistenceManager instance or a database connection); the factory acts as a repository
for long-lived resources—e.g. data in a second-level cache or connections in a connection
pool—and closing the factory interferes with its function as a repository. An example of
when you should close a PersistenceManagerFactory might be when undeploying or
redeploying the application in a managed environment (so that e.g. references to third-
party 2nd level caches are closed).
The default rules for storing package-level metadata were also changed. Rather than
being saved with the package name and a .jdo extension, the metadata file is actually now
called “package.jdo.”
Finally, two exceptions were added: ObjectNotFoundException and
OptimisticVerificationException.
Enhancement, Awareness, and Metadata

4
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

To make your object persistence as efficient and transparent as possible, the JDO
standard requires that the Java class be “enhanced” so that it can track its state,
dynamically load data, and so on. This is an unobtrusive process—for example, it won’t
interfere with your debugging. But you should be aware that you must enhance the class
files prior to use. (All JDO enhancers should be compatible; you can use MVCSoft’s
enhancer with other products, and vice versa. You can also manually implement the
required behavior of the class, rather than use an enhancer, although the benefits of this
approach are nebulous.)
There is also something called “persistence awareness.” This is just a way to handle the
case where a non-persistent class directly accesses a (non-private) field in a persistent
class. To give the runtime a chance to dynamically load data and track state changes, it
must make a small modification to the non-persistent class as well. In other words, this
non-persistent class must be made “persistence aware.” It is only necessary for those
cases where a non-persistent class directly accesses a field (not a method) in a persistent
class.
The programmer defines the behavior of the JDO runtime by specifying metadata in
XML files. Some of this data is required by the JDO specification, and some of it is
specific to a particular implementation. The MVCSoft JDO Manager provides a GUI to
edit these files (as well as to enhance class files, create database tables, and so on). The
MVCSoft product also provides an Xdoclet task for generating the metadata and Ant
tasks for enhancing class files and creating database tables.
The specification requires that the class metadata must be available at runtime to the JDO
persistence engine. Rather than read the XML files at runtime, the MVCSoft JDO
product compiles them down to a single Java class that contains all the metadata. You
provide the name of this class file as one of the configuration properties for the
javax.jdo.PersistenceManagerFactory.

Every conformant JDO product uses enhanced class files (or the “modified
source” equivalent). Every product has the concepts of persistent-capable
classes and persistent-aware classes. Every product uses standard .jdo
metadata files. Compiling the.jdo files into a Java .class file is an
MVCSoft innovation, but this is a deployment issue that does not affect
the portability of your code in any way.

Types of Identity
In an application that uses a database, a row of data is associated with a primary key. This
primary key represents that row’s identity. The JDO specification has a corresponding
concept, in which each persistent Java object has an associated id object that represents
that object’s persistent identity.
The JDO specification defines three types of identity: application identity, datastore
identity, and nondurable identity. (Nondurable identity is a special case that does not
provide a primary key in the datastore, and is not supported by the MVCSoft product.)

5
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Datastore Identity
With datastore identity, the primary key is provided by the runtime. This concept is
similar to the “auto-increment” or “sequence” feature available from many databases. A
consensus exists in the development community that—unless you have a reason to the
contrary—you should choose to use datastore identity.
The MVCSoft JDO Toolkit currently supports two algorithms for producing these keys: a
highly-configurable UUID generator and a database table-based “high-low” generator.
Application Identity
With application identity, one or more fields in the persistent Java class define the
persistent key. The developer declares these fields in the datastore and also provides a
Java key class, the rules for which will be enumerated later.
Second Class Objects
Java objects can have fields of mutable types. Whereas an immutable type cannot be
modified after construction, a mutable type can have its value changed. The JDO runtime
needs to be able to track these changes regardless of how they are made, but mutable Java
classes such as java.util.Date or java.util.LinkedList do not have any notification
mechanism that could be used by the runtime to become aware of changes.
To deal with this requirement, the JDO specification allows the runtime to replace these
mutable types with “second class persistent objects.” These will be objects that extend the
basic mutable Java type. For the most part, this replacement is invisible to the
programmer. However, these second class objects (SCOs) are only good until the object
state is flushed to the database. At that point they are replaced by new instances of second
class objects, so you can’t cache them outside the persistent Java object.
Persistence by Reachability
To make an object persistent, the programmer calls the PersistenceManager method
makePersistent(). However, he or she does not need to call this method for each related
object. Instead, the runtime engine will find any connected objects (directly in fields, or
indirectly in collections in fields) and make them persistent automatically. This is called
“persistence by reachability,” meaning that any object reachable by field navigation
starting from a persistent object is itself made persistent.

6
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

A Simple Persistent Class Example


Using JDO as a persistence mechanism does not require complicated code, or a lot of
code. This example will demonstrate the following simple techniques:
♦ How to set up a JDO project using the MVCSoft New Project Wizard
♦ How to create a persistent instance in the datastore
♦ How to find persistent class using an extent
♦ How to find a persistent class using a query
♦ How to find a persistent class by id
♦ How to delete a persistent class from the datastore
The Persistent Class
There are few requirements for a user class to be persistent. It doesn’t need to implement
java.io.Serializable or any other interface, nor does it need to extend a particular base
class.
package com.mvcsoft.jdosamples.basics.simplest;

public class MySimpleClass


{
private String someValue;

public String getSomeValue()


{
return someValue;
}

public void setSomeValue(String someValue)


{
this.someValue = someValue;
}
}

Preparing the MVCSoft JDO GUI for Use


MVCSoft does not redistribute tools.jar, so you must copy it from your Java installation
to the lib directory of the MVCSoft JDO installation directory. Make sure that the
tools.jar file comes from the same version of Java that you will be using to run the
MVCSoft JDO GUI.
Configuring a Project for MySimpleClass Using the New
Project Wizard

7
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

After you’ve written and compiled this class, there are only a few steps remaining to use
it as a persistent class. You must (1) provide the configuration data that will dictate the
class’s behavior; (2) enhance the class; (3) generate the MVCSoft metadata; and (4)
create the database tables. None of these steps are hard or time-consuming. With a simple
ad hoc test or example, you can start a new project, provide the configuration, generate
the metadata, enhance the class, and create the database table in under 30 seconds. We’ll
go through a complete example here.

The class MySimpleClass can be used with any vendor’s JDO product.
Each vendor will have their own development environment, command-
line tools, ant tasks, etc. In this section we’re working through one way to
enhance and deploy it using MVCSoft’s flexible toolset.

In the MVCSoft GUI, a project encompasses a list of persistence capable and aware
classes, and all the settings needed to enhance these classes (or make them persistence
aware) and generate the metadata they need at runtime. The MVCSoft development
environment gives you many different configuration options to work the way you want
to work. All these options are described in depth later; for now—after compiling
MySimpleClass—let’s set up the basics using the New Project Wizard. (Note that there
is an even simpler option called the New Project Wizard (Simple). This is valuable if
you have a single directory that you want to use with JDO, and are happy with most of
the MVCSoft defaults.)
First, launch the MVCSoft GUI by typing java –jar mvcjdoall.jar from the root directory
of the MVCSoft installation. Now from the File menu, select the New Project Wizard…
option. The wizard dialog will appear.

Anything that can be done using the New Project Wizard can also be done
in the Project Configuration Dialog (and much more). The New Project
Wizard is just a simpler, linear interface to this functionality. An even
simpler interface to this functionality can be found in the three-step New
Project Wizard (Simple).

New Project Wizard : Set Metadata Name


As mentioned earlier, MVCSoft generates a metadata class as a “deployment” step for
your application. This metadata class contains all the information the runtime will need to
create tables, move data back and forth from your application to the database, transform
data, cache data, and so forth. (Although the JDO specification requires you to deploy
your classes with associated XML data—and you should do this for portability—it does
not happen to be a requirement of the MVCSoft runtime to have this XML data available
at runtime.)
At this step in the New Project Wizard, you should specify a package name and a class
name for the metadata class that the MVCSoft tools generate. You can assign any

8
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

package and class name you would like. The only requirement is that you provide these
names when you configure a PersistenceManagerFactory.
For this sample code, specify com.mvcsoft.jdosamples.basics.simplest as the package
name, and MyGeneratedData as the class name. Hit the Next button.
New Project Wizard : Set Target Database
MVCSoft allows you to choose an XML file with default type and transformation
mappings for those fields for which you do not specify mappings. The choices presented
to you are all those files in the config directory for the IDE. The files shipped by
MVCSoft provide reasonable defaults for some target databases. Please note that you can
prepare metadata files for multiple target databases for distribution to your customers.
New Project Wizard : Set .jdo Format
The JDO metadata files are XML files with the extension .jdo. You can store your JDO
metadata in one project-level file, in one file per package, or in one file per class—
whichever is more convenient in your development environment. (All these options are
allowed by the spec.) Rather than providing values here, just accept the default (one file
per package) by hitting the Next button.
New Project Wizard : Add Directories and JARs for .class Files
One important configuration you will always need to do with a new project is to specify
one or more class sources. Think of this as a classpath for your persistence-capable files.
A class source can be a directory or a JAR.
For this example, add the root directory into which you compiled “MySimpleClass.” In
other words, if “MySimpleClass” is in the directory
c:\mytest\bytecode\com\mvcsoft\jdosamples\basics\simplest\, add the directory
c:\mytest\bytecode. Give a descriptive name to the class source—you will use it in the
next step. (In this case, call it “mydirectory.”)
New Project Wizard : Add Directories and JAR Libraries
Sometimes your persistent capable classes will depend on classes in other directories and
JARs. You can specify these directories and JARs on this page. (For this example, we do
not need to specify any libraries.)
New Project Wizard : Set Target Directory or JAR for Metadata
The metadata class that the MVCSoft deployment tools will generate and compile can be
placed in any non-library directory or JAR that you have specified. In this way, it will be
available at runtime.
For this example, select the “mydirectory” class source from the combo box.
New Project Wizard : Set Source Directory for .jdo Files

9
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

For maximum portability, the spec requires that the metadata files be available to be
loaded as a resource by the same class loader as the class. As a result, the default
behavior of the MVCSoft GUI is to store the metadata files with your .class files.
However, this is not an ideal setup for use in a development environment that uses
version-control software. You can instead specify that all the .jdo files are saved in a
particular directory (e.g. with the relevant .java files).
For this example, accept the default by hitting the Next button.
New Project Wizard : Set Deploy Directory for Metadata and Enhanced Classes
As described earlier, the JDO specification requires that changes be made to classes that
are going to be persistent. You can ask the MVCSoft development environment to make
these changes “in place,” by writing over the classes in your “class source” directories
and JARs. Or, if you’d prefer, you can ask the development environment to make copies
of these directories and JARs, and only make the changes in the copies. (Your generated
metadata class will be placed in whichever JARs and directories the enhanced classes are
placed.)
For this example, accept the default by hitting the Next button.
This is the last wizard step, and you can now hit the Finish button.
Selecting Persistence Capable Classes
Once you have specified the class source containing “MySimpleClass,” you must mark it
as persistence-capable. There are three ways to indicate that a class should be persistence
capable. First, you can navigate to it in the “All Objects” project tree, right-click it, and
select “Make Persistence Capable.” Second, you can use the dialog from the Edit/Edit
List of Managed Classes… option. This presents a grid of all the possible classes and lets
you set their status as persistence capable, persistence aware, or unmanaged. Third, you
can add a class based on its simple class name—rather than fully-qualified name—by
using the “Make PC Class” dialog accessible by typing Ctrl-N or choosing the Edit/Add
PC Class… menu option.
We’ll leave the settings (like table names, field names, and key types) to their defaults.

Note that there are five filters for the project tree. Of these five filters, only
the “All Objects” view will show you objects not yet declared as
persistent-capable or persistent-aware. This is the view that you must use
when you want to make a new Java class persistent-capable through the
menu in the tree pane.

Saving the Project


Once you’ve finished these configurations, you’ll want to save them. Choose File/Save
Project or File/Save Project As, and select a name for the project XML file. Two other
project files will also be saved—one listing the persistence capable classes and one listing

10
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

the persistence aware classes. These will be given default names, although you can
override them. Finally, XML files will be saved at the class or package level to save
individual class settings.
Enhancing MySimpleClass and Generating Metadata
Once you’ve set up the project, it is easy to enhance your persistence-capable and –aware
classes and to generate the MVCSoft metadata.
Enhance the classes by choosing “Enhance Classes” from the “Deploy” menu.
Generate and compile the metadata by choosing “Generate and Compile Descriptor”
from the “Deploy” menu.
Creating the Database Tables
Create the database tables by choosing the menu item Database/Create Tables. In the
dialog, specify the configuration options for your database: the JDBC driver, driver JAR,
db URL, user name, and password. You can also change the default Metadata class name
(which is picked up from the project settings file). You can also add additional
configuration properties for the PersistenceManagerFactory that is created internally by
the tool when DDL is issued. (See the chapter on PersistenceManagerFactory
configuration options for more information.)
Press the “Create” button to create the database tables.

We’re now ready to use the enhanced version of MySimpleClass (along


with the corresponding database table and the generated MVCSoft
metadata class). We’ll look at how to create a persistent instance; find that
instance using extents, queries, or ids; and delete that instance.
Except for some configuration properties (that could be loaded from a
file), everything in these examples is 100% portable. There are no
MVCSoft imports here. Furthermore, you can write, compile, and run
these classes without further reference to any part of the MVCSoft toolkit.
You don’t need to enhance these classes, or change the metadata, or
anything else.
Obviously, you do need the MVCSoft runtime and support libraries on
your classpath…

Creating a Persistent Instance of MySimpleClass


There are six simple steps to creating a persistent instance of a user class using JDO.
First, get a PersistenceManagerFactory instance from a list of user properties or from the
JNDI namespace. Second, get a PersistenceManager instance from the
PersistenceManagerFactory. Third, begin a transaction. Fourth, instantiate an instance of
the user class and call makePersistent. Fifth, commit the transaction. Sixth, clean up any

11
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

resources used. Here is the code to do this (note that this code does not have to be
enhanced or made aware):
package com.mvcsoft.jdosamples.basics.simplest;

import javax.jdo.*;
import java.util.Properties;

public class CreateInstance


{
The javax.jdo.PersistenceManagerFactoryClass property’s will always be
com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory (if you are
using MVCSoft’s product). The com.mvcsoft.jdo.MetaDataClass should be the name of
the class you specified in the mapping tool. The database properties should be
appropriate for your target database; these example properties are for the open source
database SapDB. There are other possible properties to configure various aspects of the
JDO runtime’s behavior. (Note that the best practice is to load these properties from a
file; they are hard-coded here to make the example simpler.)
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.simplest.MyGeneratedData" );

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}
The following is the standard portable technique to get an instance of a
PersistenceManagerFactory from a list of properties.
private static PersistenceManager getStandardPersistenceManager()
{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;

12
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

try
{
Step two: get the PersistenceManager from the PersistenceManagerFactory.
pm = getStandardPersistenceManager();
Step three: begin a transaction.
pm.currentTransaction().begin();
Step four: instantiate an instance of the user class and call makePersistent.
MySimpleClass mySimpleClass = new MySimpleClass();
mySimpleClass.setSomeValue("test value");
pm.makePersistent(mySimpleClass);
Step five: commit the transaction.
pm.currentTransaction().commit();
}
Step six: clean up all resources.
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}

Running the Example : Required JARs on the Classpath


Here are the jars from the MVCSoft lib directory you need on your classpath if you use
the corresponding functionality (or you can almost always substitute a later version):
♦ mvcjdoruntime.jar
♦ jdo.jar (javax.jdo packages)
♦ jboss-jdbc_ext.jar (definition of javax.sql.DataSource)
♦ jta.jar (javax.transaction packages)

13
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ log4j-1.2.7.jar (log4j logging)


♦ parser.jar (JLex and Cup for parsing JDO-QL queries)
♦ Regex.jar (used for regular expressions in transformations)
♦ commons-collections.jar (used for connection pooling in an unmanaged environment)
♦ commons-dbcp.jar (connection pooling)
♦ commons-pool.jar (connection pooling)
Finding an Existing Class Using an Extent
An extent provides access to iterators over a type of class. (You can designate whether or
not subclasses should be included.) Iteration over an entire class’s extent is rarely the
ideal technique to find a particular instance. Extents are more often used as parameters to
queries. However, here is the technique:
package com.mvcsoft.jdosamples.basics.simplest;

import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;

public class FindExistingInstanceFromExtent


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.simplest.MyGeneratedData" );

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

14
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();
The following demonstrates the use of the Extent class.
Extent extent = pm.getExtent(MySimpleClass.class, false );
Iterator iter = extent.iterator();
while (iter.hasNext())
{
MySimpleClass mySimpleClass = (MySimpleClass) iter.next();
System.out.println("MySimpleClass:" +
mySimpleClass.getSomeValue());
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}

Finding an Existing Instance Using a Query


A query is a common method for finding one or more instances that match a particular set
of criteria. A query operates on a particular class type, over the class extent or a
collection of instances. The following is a very simple example of a query.

You should obviously not expect to understand how the query mechanism
works from this example alone. Queries are explained in greater depth
later in this manual.

15
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

package com.mvcsoft.jdosamples.basics.simplest;

import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;
import java.util.Collection;

public class FindExistingInstanceFromQuery


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.simplest.MyGeneratedData" );

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();
A query instance is retrieved from the PersistenceManager instance. In this case it is
configured to operate over the extent for MySimpleClass, with a filter that tests the value
of the MySimpleClass field “someValue.”
Query query = pm.newQuery();
query.setClass(MySimpleClass.class);
Extent extent = pm.getExtent(MySimpleClass.class, true );
query.setCandidates(extent);
query.setFilter("someValue==\"test value\"");

16
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Collection results = (Collection) query.execute();


for (Iterator iterator = results.iterator(); iterator.hasNext();)
{
MySimpleClass mySimpleClass = (MySimpleClass) iterator.next();
System.out.println("MySimpleClass:" +
mySimpleClass.getSomeValue());
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}

Find an Existing Instance by Object ID


The third mechanism for finding existing instances is to call the PersistenceManager’s
getObjectById method. To demonstrate this technique, the example code gets the object
ids from the extent in a prior transaction.
package com.mvcsoft.jdosamples.basics.simplest;

import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;
import java.util.Collection;
import java.util.ArrayList;

public class FindExistingInstanceById


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.simplest.MyGeneratedData" );

17
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
Collection ids = new ArrayList();

This transaction gets a collection of object ids for use in the getObjectById mechanism.
pm.currentTransaction().begin();
Extent tempExtent = pm.getExtent(MySimpleClass.class, true);
Iterator tempIter = tempExtent.iterator();
while (tempIter.hasNext())
{
MySimpleClass mySimpleClass = (MySimpleClass) tempIter.next();
ids.add( JDOHelper.getObjectId(mySimpleClass));
}
pm.currentTransaction().commit();

Here we use the ids to re-retrieve the instances.


pm.currentTransaction().begin();
for (Iterator iterator = ids.iterator(); iterator.hasNext();)
{
Object oid = iterator.next();
MySimpleClass mySimpleClass = (MySimpleClass)
pm.getObjectById(oid, false);
System.out.println("MySimpleClass:" +
mySimpleClass.getSomeValue());
}
pm.currentTransaction().commit();
}

18
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

catch (JDOException exception)


{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close(); }
}
}

Delete All Instances


An instance’s representation is not deleted from the database just because the instance
goes out of scope, is garbage collected, or its program ends. To delete the representation,
the programmer must call the deletePersistent method of the PersistenceManager. The
following program iterates the extent to get all the instances in the database, and deletes
those instances.
package com.mvcsoft.jdosamples.basics.simplest;

import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;

public class DeleteAllInstances


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.simplest.MyGeneratedData" );

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;

19
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();
Get the extent and iterate all the instances, deleting each instance from the database.
Extent extent = pm.getExtent(MySimpleClass.class, false );
Iterator iter = extent.iterator();
while (iter.hasNext())
{
pm.deletePersistent(iter.next());
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close(); }
}
}

20
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Relationships
Relationships are a key concept in JDO. There are two types of relationships that this
chapter will discuss: inheritance relationships between classes (aka “is-a”), and
aggregation or containment relationships (aka “has-a”). Object-relational mapping
capabilities are outside the scope of the JDO specification, but using the MVCSoft JDO
toolkit, both types of relationships can be mapped to a normalized database table
structure.
Inheritance
The Java Data Objects specification allows for the use of inheritance in persistence
capable objects. Relationships, queries, and create/read/update/delete operations all work
correctly with inheritance.
The MVCSoft JDO engine maps this Java-language inheritance to data model inheritance
in the database. In other words, each class in a hierarchy is mapped to a normalized table
with a migrated key. The class type is stored in a discriminator field in the base table.
You can specify how the discriminator values map to individual class instances, or accept
the default (which uses the class name as a discriminator string).
Persistent Classes for Inheritance Example
Let’s consider an example with a tree of seven data objects (a base class, two “middle”
classes, and four most-derived classes):
BaseClass-->BranchAMiddle->BranchAMostDerivedLeft
BaseClass-->BranchAMiddle->BranchAMostDerivedRight
BaseClass-->BranchBMiddle->BranchBMostDerivedLeft
BaseClass-->BranchBMiddle->BranchBMostDerivedRight
Those classes are quite simple. (Again, notice how JDO does not impose particular
requirements on those classes.) Here is the base class:
package com.mvcsoft.jdosamples.basics.inheritance;

public class BaseClass


{
private String baseClassField;

public BaseClass()
{
}

public BaseClass(String baseClassField)


{
this.baseClassField = baseClassField;
}

21
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public String getBaseClassField()


{
return baseClassField;
}

public void setBaseClassField(String baseClassField)


{
this.baseClassField = baseClassField;
}
}
Here is the first “middle layer” class:
package com.mvcsoft.jdosamples.basics.inheritance;

public class BranchAMiddle extends BaseClass


{
private String branchAField;

public BranchAMiddle()
{
}

public BranchAMiddle(String baseClassField, String branchAField)


{
super(baseClassField);
this.branchAField = branchAField;
}

public String getBranchAField()


{
return branchAField;
}

public void setBranchAField(String branchAField)


{
this.branchAField = branchAField;
}
}
Here is the second “middle layer” class:
package com.mvcsoft.jdosamples.basics.inheritance;

public class BranchBMiddle extends BaseClass


{
private String BranchBMiddleField;

public BranchBMiddle()
{
}

public BranchBMiddle(String baseClassField, String


branchBMiddleField)

22
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
super(baseClassField);
BranchBMiddleField = branchBMiddleField;
}

public String getBranchBMiddleField()


{
return BranchBMiddleField;
}

public void setBranchBMiddleField(String branchBMiddleField)


{
BranchBMiddleField = branchBMiddleField;
}

}
Here is the first of four most-derived classes:
package com.mvcsoft.jdosamples.basics.inheritance;

public class BranchAMostDerivedLeft extends BranchAMiddle


{
private String BranchAMostDerivedLeftField;

public BranchAMostDerivedLeft()
{
}

public BranchAMostDerivedLeft(String baseClassField, String


branchAField, String branchAMostDerivedLeftField)
{
super(baseClassField, branchAField);
BranchAMostDerivedLeftField = branchAMostDerivedLeftField;
}

public String getBranchAMostDerivedLeftField()


{
return BranchAMostDerivedLeftField;
}

public void setBranchAMostDerivedLeftField(String


branchAMostDerivedLeftField)
{
BranchAMostDerivedLeftField = branchAMostDerivedLeftField;
}

}
Here is the second of four most-derived classes:
package com.mvcsoft.jdosamples.basics.inheritance;

public class BranchAMostDerivedRight extends BranchAMiddle


{

23
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

private String BranchAMostDerivedRightField;

public String getBranchAMostDerivedRightField()


{
return BranchAMostDerivedRightField;
}

public BranchAMostDerivedRight()
{
}

public BranchAMostDerivedRight(String baseClassField, String


branchAField, String branchAMostDerivedRightField)
{
super(baseClassField, branchAField);
BranchAMostDerivedRightField = branchAMostDerivedRightField;
}

public void setBranchAMostDerivedRightField(String


branchAMostDerivedRightField)
{
BranchAMostDerivedRightField = branchAMostDerivedRightField;
}

}
Here is the third of four most-derived classes:
package com.mvcsoft.jdosamples.basics.inheritance;

public class BranchBMostDerivedLeft extends BranchBMiddle


{
private String BranchBMostDerivedLeftField;

public BranchBMostDerivedLeft()
{
}

public BranchBMostDerivedLeft(String baseClassField, String


branchBMiddleField, String branchBMostDerivedLeftField)
{
super(baseClassField, branchBMiddleField);
BranchBMostDerivedLeftField = branchBMostDerivedLeftField;
}

public String getBranchBMostDerivedLeftField()


{
return BranchBMostDerivedLeftField;
}

public void setBranchBMostDerivedLeftField(String


branchBMostDerivedLeftField)
{
BranchBMostDerivedLeftField = branchBMostDerivedLeftField;
}

24
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

}
Here is the fourth of four most-derived classes:
package com.mvcsoft.jdosamples.basics.inheritance;

public class BranchBMostDerivedRight extends BranchBMiddle


{
private String BranchBMostDerivedRightField;

public BranchBMostDerivedRight()
{
}

public BranchBMostDerivedRight(String baseClassField, String


branchBMiddleField, String branchBMostDerivedRightField)
{
super(baseClassField, branchBMiddleField);
BranchBMostDerivedRightField = branchBMostDerivedRightField;
}

public String getBranchBMostDerivedRightField()


{
return BranchBMostDerivedRightField;
}

public void setBranchBMostDerivedRightField(String


branchBMostDerivedRightField)
{
BranchBMostDerivedRightField = branchBMostDerivedRightField;
}
}
Set up the project as before. You do not need to individually configure any of the classes
after adding them to the project (although you can). Enhance the classes (nothing need be
made persistence aware), and generate and compile the metadata class. Note the name
you give your metadata class, and ensure that you add this to your
PersistenceManagerFactory.
Creating One Example of Each Class
The following program creates an instance of each object in the inheritance hierarchy.
Note that no special effort is required to use inheritance here; the code with inheritance is
indistinguishable from the code without it.
package com.mvcsoft.jdosamples.basics.inheritance;

import javax.jdo.*;
import java.util.Properties;

public class CreateInstances


{
private static Properties getStandardProperties()
{

25
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Properties properties = new Properties();


properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.inheritance.MyGeneratedData" );

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

BaseClass baseClass = new BaseClass("base1");


BranchAMiddle aMiddle = new BranchAMiddle("base2", "aMiddle2");
BranchAMostDerivedLeft aMostDerivedLeft = new
BranchAMostDerivedLeft("base3", "aMiddle3", "aMostDerivedLeft");
BranchAMostDerivedRight aMostDerivedRight = new
BranchAMostDerivedRight("base4", "aMiddle4", "aMostDerivedRight");
BranchBMiddle bMiddle = new BranchBMiddle("base5", "aMiddle5");
BranchBMostDerivedLeft bMostDerivedLeft = new
BranchBMostDerivedLeft("base6", "bMiddle6", "bMostDerivedLeft");
BranchBMostDerivedRight bMostDerivedRight = new
BranchBMostDerivedRight("base7", "bMiddle7", "bMostDerivedRight");

pm.makePersistent(baseClass);
pm.makePersistent(aMiddle);
pm.makePersistent(aMostDerivedLeft);
pm.makePersistent(aMostDerivedRight);
pm.makePersistent(bMiddle);
pm.makePersistent(bMostDerivedLeft);
pm.makePersistent(bMostDerivedRight);

26
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}
Iterating Classes With or Without Subclasses
When you acquire an extent for a class, you can specify whether or not subclasses should
be included. The following example shows each way (with and without subclasses).
package com.mvcsoft.jdosamples.basics.inheritance;

import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;

public class FindAllInstances


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.inheritance.MyGeneratedData" );

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

27
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

System.out.println("*** Without subclasses ***");


Extent extentWithoutSubclasses = pm.getExtent(BaseClass.class,
false );
Iterator iterWithout = extentWithoutSubclasses.iterator();
while (iterWithout.hasNext())
{
BaseClass baseClass = (BaseClass) iterWithout.next();
StringBuffer buffer = new
StringBuffer(baseClass.getClass().getName());
buffer.append(";baseClassField=" +
baseClass.getBaseClassField());
System.out.println(buffer.toString());
}

System.out.println("*** With subclasses ***");


Extent extentWithSubclasses = pm.getExtent(BaseClass.class, true
);
Iterator iterWith = extentWithSubclasses.iterator();
while (iterWith.hasNext())
{
BaseClass baseClass = (BaseClass) iterWith.next();
StringBuffer buffer = new
StringBuffer(baseClass.getClass().getName());
buffer.append(";baseClassField=" +
baseClass.getBaseClassField());
System.out.println(buffer.toString());
}

pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{

28
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}
Queries and Inheritance
Just like in the last example, you can specify that a query does or does not include
subclasses—by setting the candidate extent to include subclasses or not. You can cast a
value to a subclass in the query filter. If the candidate is not an instance of that class, the
expression will return false.
package com.mvcsoft.jdosamples.basics.inheritance;

import com.mvcsoft.jdosamples.basics.simplest.MySimpleClass;

import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;
import java.util.Collection;
import java.util.ArrayList;

public class QueryInstances


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.inheritance.MyGeneratedData" );

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()

29
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();
Query query = pm.newQuery();
query.setClass(BaseClass.class);
Extent extentWithSubclasses = pm.getExtent(BaseClass.class, true
);
query.setCandidates(extentWithSubclasses);

query.setFilter("((BranchAMostDerivedLeft)this).branchAMostDerivedLeftF
ield==\"aMostDerivedLeft\" |
((BranchAMostDerivedRight)this).branchAMostDerivedRightField==\"aMostDe
rivedRight\"");
System.out.println("********With Subclasses********");
Collection resultsWith = (Collection) query.execute();
for (Iterator iterator = resultsWith.iterator();
iterator.hasNext();)
{
BaseClass baseClass = (BaseClass) iterator.next();
StringBuffer buffer = new
StringBuffer(baseClass.getClass().getName());
buffer.append(";baseClassField=" +
baseClass.getBaseClassField());
System.out.println(buffer.toString());
}
Extent extentWithoutSubclasses = pm.getExtent(BaseClass.class,
false );
query.setCandidates(extentWithoutSubclasses);

//query.setFilter("((BranchAMostDerivedLeft)this).branchAMostDerivedLef
tField==\"aMostDerivedLeft\" |
((BranchAMostDerivedRight)this).branchAMostDerivedRightField==\"aMostDe
rivedRight\"");
System.out.println("********Without Subclasses********");
Collection resultsWithout = (Collection) query.execute();
for (Iterator iterator = resultsWithout.iterator();
iterator.hasNext();)
{
BaseClass baseClass = (BaseClass) iterator.next();
StringBuffer buffer = new
StringBuffer(baseClass.getClass().getName());
buffer.append(";baseClassField=" +
baseClass.getBaseClassField());

30
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

System.out.println(buffer.toString());
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}
Deleting Instances in an Inheritance Hierarchy
You do not need to do anything special to delete instances in an inheritance hierarchy.
Deleting the instance removes the record from all the relevant tables.
package com.mvcsoft.jdosamples.basics.inheritance;

import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;

public class DeleteAllInstances


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.inheritance.MyGeneratedData" );

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

31
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();
Extent extent = pm.getExtent(BaseClass.class, true );
Iterator iter = extent.iterator();
while (iter.hasNext())
{
pm.deletePersistent(iter.next());
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}

Aggregation and Containment


The JDO specification allows for fields of type java.util.Collection, java.util.Set, and any
persistent capable object. It optionally allows (and MVCSoft supports) array types and
types of java.util.List, java.util.Map, java.util.ArrayList, java.util.HashMap,

32
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

java.util.Hashtable, java.util.LinkedList, java.util.TreeMap, java.util.TreeSet, and


java.util.Vector.
As long as the contents of these fields or collections are homogenous, the MVCSoft JDO
engine can map these relationships to normalized database tables, using either foreign
keys or relationship tables. (Collections or fields that allow heterogenous types can be
mapped to serialized fields, although this is rarely a good idea.)
The Difference Between Aggregation and Containment
Containment is a stronger relationship than aggregation, because contained objects do not
have an independent existence apart from their containing objects. For an example of
containment, in an order-entry system you might have line items that should only exist
while their parent order exists. However, that order might reference a product that should
exist regardless of whether or not the order exists (aggregation). To implement
containment, you need the ability to cascade a delete to the contained objects. You can
implement this using JDO callbacks, described later.
Second Class Objects
Multi-valued relationships in Java are implemented using collection classes that
implement java.util.Collection or java.util.Map. In JDO, instances of these collection
classes are often implemented as second-class objects (although the specification allows
them to be first class objects.)
Here is an example of a persistent object with a field that would be replaced by a second
class object (the java.util.Date field).
package com.mvcsoft.jdosamples.basics.scobehavior;

import javax.jdo.*;
import java.util.Properties;
import java.util.Calendar;
import java.util.Date;
import java.text.DateFormat;

public class DemonstrateSCOBehavior


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.scobehavior.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");

33
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
Object oid;
// create an instance with a date value
Create an instance and set its initial values. Note that we can use a plain java.util.Date to
set the field value.
pm.currentTransaction().begin();
SomeDateObject someDateObject = new SomeDateObject();
someDateObject.setSomeString("Some String");
Calendar calendar = Calendar.getInstance();
calendar.set(1977, 11, 20, 10, 15, 30);
Date time = calendar.getTime();
someDateObject.setUtilDate(time);
pm.makePersistent(someDateObject);
oid = JDOHelper.getObjectId(someDateObject);
pm.currentTransaction().commit();
Retrieve the instance. The date class will actually be a subclass of java.util.Date. (The
exact default class is com.mvcsoft.jdo.runtime.sco.SCOUtilDate.) The programmer can
ignore this detail, and pretend he or she is just working with a java.util.Date instance. We
modify the date instance directly, but of course we could also just call
someDateObject2.setUtilDate() instead. You certainly don’t need to do both.
// get the object fresh; set the date through the SCO
pm.currentTransaction().begin();
SomeDateObject someDateObject2 = (SomeDateObject)
pm.getObjectById(oid, true);
Calendar calendar2 = Calendar.getInstance();
calendar2.set(1985, 5, 11, 10, 15, 30);
Date utilDate = someDateObject2.getUtilDate();
System.out.println("Date class:" + utilDate.getClass());
utilDate.setTime(calendar2.getTime().getTime());
pm.currentTransaction().commit();
We ensure that the change was registered by the runtime.

34
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

// get the object fresh; check to see if the date has changed
pm.currentTransaction().begin();
SomeDateObject someDateObject3 = (SomeDateObject)
pm.getObjectById(oid, true);
Date utilDate2 = someDateObject2.getUtilDate();
DateFormat dateFormat = DateFormat.getDateTimeInstance();
String dateString = dateFormat.format(utilDate2);
System.out.println("Date:" + dateString);
pm.currentTransaction().commit();

}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

}
Because the Java language does not treat arrays as true objects, it is not possible for the
runtime to provide second class objects for them. If you make a change to an array, you
should let the JDO runtime know about this change by calling the makeDirty method of
the JDOHelper class, as follows:
JDOHelper.makeDirty( persistentObj, “arrayFieldName” );
Mapping Relationships
You have the option of mapping relationships in a serialized column, or in normalized
form in the database. Most projects should always choose a normalized relational
mapping. The only exception is if you have a relationship with heterogenous objects that
cannot be mapped to a standard relational schema—e.g. if you have a collection class
with instances of java.lang.String, com.mycompany.MyUserObject, java.lang.Integer,
and com.mycompany.MyUnrelatedUserObject. (Note that heterogenous objects in an
inheritance hierarchy with common base persistent object can easily be mapped to a
normalized schema.)

35
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Users familiar with the EJB 2.0 container-managed persistence


specification may expect the option of having bi-directional relationships
managed by the runtime. At least in the first release of JDO, the spec
writers felt that this interfered with their goal to provide transparent object
persistence.
The MVCSoft JDO Toolkit provides the option of having bi-directional
relationships. These are mapped to the same table or foreign key in the
database. However, unlike with EJB 2.0 container-managed persistence,
you must set both ends of the relationship at runtime (just as if they were
not being stored persistently by a JDO product).

Configuring Classes With Relationships


To specify a mapping for a single-valued relationship, you should specify at least the
following two pieces of information for the field: “Map As”—the choices are
“Relationship” or “Column(s);” and “Value Type”—the choices are those persistent
classes that might be assigned to the field.
To specify a mapping for a many-valued relationship, you should specify this
information, and you can also provide a particular “second-class object” implementation
class.
To specify a mapping for a java.util.Map-based relationship, you should also specify the
“Key Type.”
This is all done on the “Basic Info” page for the field. There are many additional
configuration options available, covered later in this documentation.
Sample Related Classes
The following classes demonstrate both a simple single-valued relationship and a
collection based multi-valued relationship. The relationships are implemented as simple
fields in the parent class, and impose no special burdens on the programmer. This is the
parent class:
package com.mvcsoft.jdosamples.basics.relationships;

import java.util.Collection;

public class SimpleParent


{
private String someParentValue;
private SimpleChild simpleChild;
private Collection simpleChildren;

public SimpleParent()
{
}

36
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public SimpleParent(String someParentValue, SimpleChild simpleChild,


Collection simpleChildren)
{
this.someParentValue = someParentValue;
this.simpleChild = simpleChild;
this.simpleChildren = simpleChildren;
}

public String getSomeParentValue()


{
return someParentValue;
}

public void setSomeParentValue(String someParentValue)


{
this.someParentValue = someParentValue;
}

public SimpleChild getSimpleChild()


{
return simpleChild;
}

public void setSimpleChild(SimpleChild simpleChild)


{
this.simpleChild = simpleChild;
}

public Collection getSimpleChildren()


{
return simpleChildren;
}

public void setSimpleChildren(Collection simpleChildren)


{
this.simpleChildren = simpleChildren;
}

}
This is the child class that particpates in the relationships:
package com.mvcsoft.jdosamples.basics.relationships;

public class SimpleChild


{
private String someChildValue;

public SimpleChild(String someChildValue)


{
this.someChildValue = someChildValue;
}

public SimpleChild()
{

37
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public String getSomeChildValue()


{
return someChildValue;
}

public void setSomeChildValue(String someChildValue)


{
this.someChildValue = someChildValue;
}
}
Creating Related Objects
This simple class demonstrates how to populate both the one-one and one-many
relationships. It is no more difficult than setting fields or adding elements to a collection.
package com.mvcsoft.jdosamples.basics.relationships;

import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
import javax.jdo.JDOException;
import java.util.Properties;
import java.util.LinkedList;

public class DemonstrateRelationshipCreate


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.relationships.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

38
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();
// individual child relationship
SimpleChild simpleChildA = new SimpleChild("someValueA");
// some children for a collection
LinkedList listChildren = new LinkedList();
SimpleChild simpleChildB1 = new SimpleChild("someValueB1");
SimpleChild simpleChildB2 = new SimpleChild("someValueB2");
SimpleChild simpleChildB3 = new SimpleChild("someValueB3");
listChildren.add(simpleChildB1);
listChildren.add(simpleChildB2);
listChildren.add(simpleChildB3);
// make the parent
SimpleParent simpleParent = new SimpleParent("someParentValue",
simpleChildA, listChildren);
// now make the parent persistent; the children will
// become persistent by reachability
pm.makePersistent(simpleParent);
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}
Loading Related Objects
Related objects are loaded by the JDO runtime without any intervention by the program.
The JDO specification does not specify whether the related data should be lazily loaded
or eagerly loaded. The MVCSoft runtime provides a variety of configuration options. If
you do nothing, the default is to lazily load related objects.

39
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This example loads all SimpleParent objects, and then prints out all their related
SimpleChild objects.
package com.mvcsoft.jdosamples.basics.relationships;

import com.mvcsoft.jdosamples.basics.scobehavior.SomeDateObject;

import javax.jdo.*;
import java.util.*;
import java.text.DateFormat;

public class DemonstrateRelationshipRead


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.relationships.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

Extent extent = pm.getExtent(SimpleParent.class, false);


Iterator iterator = extent.iterator();
while (iterator.hasNext())
{
SimpleParent simpleParent = (SimpleParent) iterator.next();

40
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

System.out.println("***Found SimpleParent with value: " +


simpleParent.getSomeParentValue());
SimpleChild simpleChild = simpleParent.getSimpleChild();
System.out.println("***Found multiplicity-one SimpleChild with
value: " + simpleChild.getSomeChildValue());
Collection simpleChildren = simpleParent.getSimpleChildren();
System.out.println("***Multiplicity-many SimpleChild instances
with values:");
for (Iterator iterChildren = simpleChildren.iterator();
iterChildren.hasNext();)
{
SimpleChild simpleChildMany = (SimpleChild)
iterChildren.next();
System.out.println(simpleChildMany.getSomeChildValue());
}
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}
Deleting Relationship Information
The JDO 1.0 specification does not define a cascade delete mechanism, but it does allow
the developer of a persistent object to add a pre-delete callback to implement cascade
deletes. This technique is described later in this documentation.
Relationship information, however, is part of the persistent state of an object, whether it
be mapped in the database to a foreign key or a relationship table. When you delete a
persistent object, the foreign keys or related objects are automatically cleared.
Relational database schemas are usually defined with foreign key constraints and not-null
constraints that require insertions and deletions to be made in a particular order. The
MVCSoft JDO runtime will reorder and combine database i/o to respect these constraints.

41
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This example shows all that is required to clear out the database information for
SimpleParent and SimpleChild objects, regardless of how you have mapped the
relationship information. Notice that all we do is delete all the SimpleParent and
SimpleChild objects, without regard to any particular order.
package com.mvcsoft.jdosamples.basics.relationships;

import javax.jdo.*;
import java.util.*;

public class DemonstrateRelationshipDelete


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.relationships.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();
// delete parents
Extent extentParent = pm.getExtent(SimpleParent.class, false);
Iterator iterParent = extentParent.iterator();
while (iterParent.hasNext())
{
pm.deletePersistent(iterParent.next());
}

42
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

// now we have to delete the children; no cascade delete!


Extent extentChildren = pm.getExtent(SimpleChild.class, false);
Iterator iterChildren = extentChildren.iterator();
while (iterChildren.hasNext())
{
pm.deletePersistent(iterChildren.next());
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}

Relationships Based on java.util.Map


The JDO specification allows, but does not require, an implementation to support
persistent fields of type java.util.Map (and its java.util subclasses). The MVCSoft JDO
runtime does support fields of java.util.Map. Furthermore, you can store the contents of
such a field in a fully normalized relationship table. (You also have the option of storing
a serialized map in a table column, although this is rarely a good idea. If you do this, the
MVCSoft runtime provides the option of automatically swizzling persistent instances to
primary keys on storage, and primary keys to persistent instances on retrieval.)
You can have maps that have keys of any fundamental or persistent type, and values of
any fundamental or persistent type. For example, you might map Strings to Integers,
Strings to instances of com.mycompany.MyObject, instances of
com.mycompany.MyObject to Doubles, or instances of com.mycompany.MyObject to
instances of com.mycompany.MyOtherObject. These are just some of the possibilities.
An Example Class With Two Maps
The following class has two maps. One will be configured as a map of java.lang.String
keys to java.lang.String values. The other will be configured as a map of instances of one
type of object to instances of another type of object. (The configuration of the map takes

43
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

place entirely in the metadata. You can configure this in the MVCSoft GUI, using the
MVCSoft XDoclet support module, or even manually.) Here is the class:
package com.mvcsoft.jdosamples.basics.maps;

public class ParentOfMaps


{
private java.util.Map strToStr;
private java.util.Map keyObjToValObj;

public java.util.Map getStrToStr()


{
return strToStr;
}

public void setStrToStr(java.util.Map strToStr)


{
this.strToStr = strToStr;
}

public java.util.Map getKeyObjToValObj()


{
return keyObjToValObj;
}

public void setKeyObjToValObj(java.util.Map keyObjToValObj)


{
this.keyObjToValObj = keyObjToValObj;
}
}
There are two other persistent classes in this example: a key class and a value class. (In a
less-manufactured example, this might be a Region class and a SalesPerson class, or a
Project class and a ProjectManager class.)
Here is our key class. One interesting feature is that we have implemented hashCode and
equals to be based on the persistent identity of the SampleKeyObject (if it has one), rather
than just depending on the default implementation. This is usually a good idea, but it is
especially recommended if you are using the persistent object as a key in maps or sets.
Frankly, in this case and most others, it will work either way because the JDO runtime
ensures that only one Java instance represents a particular persistent instance at any one
time. But it will not work if you substitute some application-specific meaning of the
method equals.
package com.mvcsoft.jdosamples.basics.maps;

import javax.jdo.JDOHelper;

public class SampleKeyObject


{
private String someValue;

public SampleKeyObject()

44
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
}

public SampleKeyObject(String someValue)


{
this.someValue = someValue;
}

public String getSomeValue()


{
return someValue;
}

public void setSomeValue(String someValue)


{
this.someValue = someValue;
}

public int hashCode()


{
if (JDOHelper.isPersistent(this))
{
return JDOHelper.getObjectId(this).hashCode();
}
else
return super.hashCode();
}

public boolean equals(Object obj)


{
if (JDOHelper.isPersistent(this))
{
return JDOHelper.isPersistent(obj) &&
JDOHelper.getObjectId(this).equals(JDOHelper.getObjectId(obj));
}
else
return super.equals(obj);
}

public String toString()


{
return "Key Object:" + someValue;
}
}
And here is our value class:
package com.mvcsoft.jdosamples.basics.maps;

public class SampleValueObject


{
private String someValue;

public SampleValueObject()
{

45
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public SampleValueObject(String someValue)


{
this.someValue = someValue;
}

public String getSomeValue()


{
return someValue;
}

public void setSomeValue(String someValue)


{
this.someValue = someValue;
}

public String toString()


{
return "Value Object:" + someValue;
}

}
Using the GUI to Configure the Maps in Class ParentOfMaps
Once you have created a new project and added the three persistent classes to the project,
you need to configure the two java.util.Map instances in class ParentOfMaps. In the tree
view, click on the fields to configure the basic information on the “Basic Info” panel for
that field. For each field, set the “Map As” value to Relationship. For the
mapStringToString field, set both the Key Type and the Value Type to java.lang.String.
For the mapKeyObjectToValueObject field, set the Key Type to
com.mvcsoft.jdosamples.basics.maps.SampleKeyObject and set the Value Type to
com.mvcsoft.jdosamples.basics.maps.SampleValueObject.
Once that is finished, you can save the project, generate and compile the metadata,
enhance the classes, and create the database tables. (All this should take less than ten
seconds, once you know where the menu items are.)
Adding Values to the Maps
As with most JDO samples, there is little difference from a program that adds values to
maps not using JDO persistence.
package com.mvcsoft.jdosamples.basics.maps;

import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
import javax.jdo.JDOException;
import java.util.Properties;
import java.util.HashMap;
import java.util.Map;

46
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public class DemonstrateMapCreate


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.maps.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

// this object has the maps


ParentOfMaps parentOfMaps = new ParentOfMaps();

// set up a string-to-string map


Map stringToString = new HashMap();
stringToString.put("key1", "value1");
stringToString.put("key2", "value2");
stringToString.put("key3", "value3");
parentOfMaps.setStrToStr(stringToString);

// set up an object-to-object map


Map objectToObject = new HashMap();
objectToObject.put(new SampleKeyObject("some key value 1"),
new SampleValueObject("some val value 1"));
objectToObject.put(new SampleKeyObject("some key value 2"),
new SampleValueObject("some val value 2"));

47
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

objectToObject.put(new SampleKeyObject("some key value 3"),


new SampleValueObject("some val value 3"));
parentOfMaps.setKeyObjToValObj(objectToObject);

pm.makePersistent(parentOfMaps);
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

}
Reading Values from Maps
This simple program finds all the parent objects, and prints out the values in their two
maps.
package com.mvcsoft.jdosamples.basics.maps;

import javax.jdo.*;
import java.util.*;

public class DemonstrateMapRead


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.maps.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");

48
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

private static void printMap(String mapName, Map map)


{
System.out.println("***Printing map:" + mapName);
if (map == null)
System.out.println("(Map was null)");
else
{
Set keys = map.keySet();
for (Iterator iterator = keys.iterator(); iterator.hasNext();)
{
Object key = iterator.next();
Object value = map.get(key);
System.out.println("Key:" + key + "; Value:" + value);
}
}
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

Extent extent = pm.getExtent(ParentOfMaps.class, false);


Iterator iterator = extent.iterator();
while (iterator.hasNext())
{
ParentOfMaps parentOfMaps = (ParentOfMaps) iterator.next();
System.out.println("***Found ParentOfMaps***");
printMap("mapStringToString", parentOfMaps.getStrToStr());
printMap("mapKeyObjectToValueObject",
parentOfMaps.getKeyObjToValObj());
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();

49
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

}
Deleting Values from Maps
You can manipulate maps in JDO persistent objects just as you would in plain Java
classes. You can add and delete key-value pairs, and clear or replace the entire map. This
example demonstrates another feature: when you delete the objects, the map tables are
cleared automatically.
package com.mvcsoft.jdosamples.basics.maps;

import javax.jdo.*;
import java.util.*;

public class DemonstrateMapDelete


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.basics.maps.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{

50
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();
// delete parents
Extent extentParent = pm.getExtent(ParentOfMaps.class, false);
Iterator iterParent = extentParent.iterator();
while (iterParent.hasNext())
{
pm.deletePersistent(iterParent.next());
}
// delete key objects
Extent extentKeys = pm.getExtent(SampleKeyObject.class, false);
Iterator iterKeys = extentKeys.iterator();
while (iterKeys.hasNext())
{
pm.deletePersistent(iterKeys.next());
}
// delete value objects
Extent extentValues = pm.getExtent(SampleValueObject.class,
false);
Iterator iterValues = extentValues.iterator();
while (iterValues.hasNext())
{
pm.deletePersistent(iterValues.next());
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();

51
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

}
}

52
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

JDO Queries
The JDO query mechanism gives your program a portable way to issue queries, without
regard to the database or even the type of data store. Of the three ways to find a JDO
instance—by ID, by iterating an extent, and by queries—the query mechanism is the
most general and probably the most important to your application. This chapter will
describe the basics of JDO queries.
There are two aspects to JDO queries: the API and the JDO query language. The API
provides the interface to submit a query to the runtime and to retrieve your results. The
query language allows you to specify what those results should be.
The JDO API
The PersistenceManager interface—the same one we have used to make objects
persistent, delete objects, find objects by id, and so forth—is used to get an instance of an
object implementing the Query interface:
Query query = persistenceManager.newQuery();
The specification requires three elements to any query. First, the class of the candidate
instances must be set. In other words, you will be performing this query on a collection
of, or extent for, a particular class. You must indicate this class as follows:
query.setClass(SomeClassOfMine.class);
Second, the candidates for the query must be specified. Often, the candidates will be
every instance of a particular class. For this common case, you use an instance of that
class’s extent:
Extent extent = pm.getExtent(SomeClassOfMine.class, true /*
include subclasses?*/);
query.setCandidates(extent);
Alternatively, you can use a java.util.Collection instance—perhaps one that was a result
from a previous query:
query.setCandidates(myCollection);
Third, you must specify the query filter. (Technically, you can allow the filter to be null,
in which case the query returns all the candidate instances…) The filter is where you use
the JDO query language:
query.setFilter("theInt == 25");
You may also optionally specify the ordering specification. This is a comma-separated
list of expressions followed by either the keyword ascending or the keyword descending.
query.setOrdering("theInt descending, theString ascending");
As part of specifying the filter, you may optionally specify parameter declarations,
variable declarations, and import statements. We’ll describe the functions of these

53
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

three things in the next section as part of our description of the query filter. Here are
example method calls so that you will be familiar with them. Here are declareImports and
declareParameters calls, with the corresponding filter that uses them:
query.declareImports("import java.util.Date;");
query.declareParameters("Date theParam");
query.setFilter("theDate == theParam");
Variables are used primarily to iterate through collection instances. Their use will be
described in greater depth in the following section on filter specifications, but here is a
quick example of the API:
query.declareVariables("RelChildForQuery relChild;
SimpleObjForQuery simpleObj");
query.setFilter("children.contains(relChild) &&
relChild.simpleObjects.contains(simpleObj) && simpleObj.theInt==1");
Once you have set up the query, you then execute it, passing values for any parameters
that you have declared. If you have three or fewer parameters, you can simply pass them
directly in the call to execute in the order in which they were declared:
// no parameters
query.execute();
// one parameter
query.execute(myParam);
// two parameters
query.execute(myParam1, myParam2);
// three parameters
query.execute(myParam1, myParam2, myParam3);
There are two other mechanisms for passing parameters, and these mechanisms allow an
arbitrary number of parameters. You can pass the parameters as an array (in the order in
which they were declared):
// array
query.executeWithArray(new Object[]{prm1, prm2, prm3, prm4});
You can also set up a map with the parameter names mapped to your parameter values:
Map map = new HashMap();
map.put("myParam1", prm1);
map.put("myParam2", prm2);
map.put("myParam3", prm3);
map.put("myParam4", prm4);
query.executeWithMap(map);
The execute method returns a java.lang.Object. In version 1.0 of the JDO spec, this will
always be an unmodifiable java.util.Collection with the results. Future versions of the
specification will undoubtedly provide other options for query results.
Ignoring Modified Instances
By default, your query will take into account any changes you have made within the
transaction. The MVCSoft JDO runtime will accomplish this by flushing your changes
out to the database. You may know that your result set will not be affected by any

54
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

pending database i/o (or it doesn’t matter to your program), so you can avoid the
overhead (and potential integrity-constraint violations if your data modifications are in an
uncompleted state) of the flush. The Query interface presents a method to give the
runtime permission to ignore pending changes: getIgnoreCache(boolean).
The “Java Method” Metaphor
The JDO query mechanism is modeled after a Java language method. This is to make it
simple for Java programmers to understand the query mechanism, rather than introducing
an entirely new query language for them to learn (as JDBC or EJB 2.0 CMP does).
Picture your query as a method in the class of your candidate instances. The return type
is a boolean result: true means include in the results and false means don’t include. You
set the parameters of the method in the declareParameters method of the Query
interface—with a comma-separated list of types and names, just as if you were writing a
Java method header. You declare any variables you’ll need with the declareVariables
method of the Query interface—with a semi-colon separated list of types and names, just
as if you were declaring them in a Java method body. You write the method body with
the setFilter method of the Query interface, and the JDO filter specification is written in a
language that is as close to Java as the spec writers could make it.
Namespaces in the query mechanism (for fields, parameters, variables, etc.) work the
same as they do in the Java language. Classes in the java.lang package are implicitly
imported, as are classes in the package of your candidate class. If you want to declare a
parameter or a variable of any other type, you can either use the fully qualified class
name or include it in the string of semi-colon separated imports using the declareImports
method.
Variables and parameters can hide the names of class fields, just as in a Java method.
You can access the hidden class field by qualifying it with the this keyword, just as in a
Java method.
The Filter Specification Query Language
The filter specification query language has a syntax very similar to the Java language. It
is not, however, used as a procedural language. Instead, a single boolean expression is
specified. Conceptually, this boolean expression is applied to each candidate class and
included in the result set if the result is true. (Of course, in reality the MVCSOft JDO
runtime translates the query into a single SQL statement…)
Similar to the Java Language
For the most part, writing this expression is trivially simple to anyone who knows Java—
or frankly, anyone who can program at all. The next few paragraphs describe capabilities
and syntax that are the same as Java, or Java with simplifications.
You can use any of the standard comparison operators, i.e. ==, !=, >, >=, <, and <=, to
compare strings, numbers, and dates (and unlike Java, you don’t need to worry about

55
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

using methods rather than operators on object types like java.lang.Integer or


java.lang.String). You can use the == and != operators on persistent objects.
You can use boolean and conditional operators such as &, &&, |, and ||. You can use
math operators such as +, -, *, and /. The + operator also works for string concatenation.
You can use parentheses to explicitly control operator precedence. You can use the cast
operator for cases where an inheritance hierarchy requires it (and rather than getting
exceptions when the cast is invalid, the relevant part of the expression will just.evaluate
to false).
Literals work just like they do in Java for integers, floating point types, boolean types,
characters, strings, and null. Type promotions happen automatically when required. You
never get null pointer exceptions because of a filter test; instead, that part of the filter
expression evaluates to false.
Navigation through single-valued relationships (i.e. fields with a persistent object type)
uses the dot-operator, just like Java. However, you never need to worry about null pointer
exceptions. If a field through which you are navigating is null for a particular instance,
that part of the filter expression simply evaluates to false.
Functions are called just like in Java. Unfortunately, the JDO spec only defines three
standard functions: startsWith and endsWith on instances of java.lang.String, and
isEmpty on instances of java.util.Collection. Fortunately, the MVCSoft JDO Toolkit
allows you to easily define your own functions, as will be described later in this chapter.
Navigating Through Multi-Valued Relationships
One major exception to a filter expression’s similarity to Java is how to navigate through
multi-valued relationships, i.e. fields with instances implementing java.util.Collection.
The JDO filter expression defines a single boolean expression, but iterating through a
collection in Java requires multiple expressions.
To solve this problem, the JDO spec writers changed the meaning of a collection class’s
contains method. In Java programming, the contains method is a simple boolean method
that indicates whether the parameter is a member of that collection. In the JDO filter
specification, this method both implicitly iterates through the collection and assigns a
value to the variable parameter for each iteration. This is the only difficult concept in
JDO queries, but it really sounds more complicated than it is. An example should make it
clear (we’ll see an entire runnable example later in this chapter). Let’s say we have three
classes: Grandparent, Parent, and Child. A Grandparent has a collection of Parent
instances, and a Parent has a collection of Child instances. Now we want to get all the
grandparents with at least one grandchild named “Sue.”
First, whenever we want to navigate through a collection, we need a variable. (That’s the
main use of variables.) Since we are navigating through two collections, we need to
declare two variables.
query.declareVariables("Parent parent; Child child");
Now, we write the filter. Remember, the Grandparent class is our candidate class:

56
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

query.setFilter("parents.contains(parent) &&
parent.children.contains(child) && child.firstName==\”Sue\”");
Conceptually, Java code is being executed that looks like this hypothetical method in the
Grandparent class:
public boolean hypotheticalFilter()
{
for (Iterator iterParents = parents.iterator();
iterParents.hasNext();)
{
Parent parent = (Parent) iterParents.next();
for (Iterator iterChildren = parent.children.iterator();
iterChildren.hasNext();)
{
Child child = (Child) iterChildren.next();
if (child.firstName.equals("Sue"))
return true;
}
}
return false;
}
Of course, what’s really happening is that this query is being translated into the
equivalent SQL statement. This syntax for navigating through multi-valued relationships
is a convenient shorthand for the above Java code.
Unconstrained Variables
You can also have variables in your filter that are not constrained using a collection’s
contains method. In this case, they are “constrained” against the entire extent of that
class.
Some JDO Query Examples
The query mechanism in JDO is quite easy to use, as should be demonstrated by the
following examples. There will be four sets of sample queries, in the following classes:
IssueSimpleQueries, IssueDotNotationAndParamQueries, IssueCollectionQueries, and
IssueUserDefinedFunctionQueries. The required persistent classes (and helper classes for
creating sample data) will be introduced with each set of samples.
First Set: Some Simple Queries
This first example will demonstrate the following query techniques:
♦ testing a string field’s value against a constant
♦ testing an int field’s value against a constant
♦ combining two tests
♦ testing a field against null
♦ using the greater than operator

57
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ using the startsWith method


♦ using the endsWith method
♦ specifying a sort order
Persistent Object SimpleObjForQuery
This simple persistent object will be the basis of the queries in this first set.
package com.mvcsoft.jdosamples.queries.simple_where;

public class SimpleObjForQuery


{
private String theString;
private int theInt;

public SimpleObjForQuery()
{
}

public SimpleObjForQuery(String theString, int theInt)


{
this.theString = theString;
this.theInt = theInt;
}

public String getTheString()


{
return theString;
}

public void setTheString(String theString)


{
this.theString = theString;
}

public int getTheInt()


{
return theInt;
}

public void setTheInt(int theInt)


{
this.theInt = theInt;
}

public String toString()


{
return "SimpleObjForTest:theString=" + theString + ";theInt=" +
theInt;
}
}
Populating Sample Values

58
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This class just creates some sample values against which we can issue our queries.
package com.mvcsoft.jdosamples.queries.simple_where;

import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
import javax.jdo.JDOException;
import java.util.Properties;

public class QueryPopulateValues


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.queries.simple_where.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();
SimpleObjForQuery simple1 = new SimpleObjForQuery("one", 25);
SimpleObjForQuery simple2 = new SimpleObjForQuery("one_two", 25);
SimpleObjForQuery simple3 = new SimpleObjForQuery(null, 45);

pm.makePersistentAll(new Object[]{simple1, simple2, simple3});

pm.currentTransaction().commit();
}

59
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

catch (JDOException exception)


{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

}
Class with Sample Queries
package com.mvcsoft.jdosamples.queries.simple_where;

import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Iterator;

public class IssueSimpleQueries


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.queries.simple_where.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");
We haven’t seen this MVCSoft property before. We’re just telling the runtime how to
map query functions to SQL functions.
properties.setProperty("com.mvcsoft.jdo.FunctionMap",
"function_maps/sapdb.properties");

60
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

Here, we set up the query with its class and candidates. We’ll use this query multiple
times by resetting the filter.
Query query = pm.newQuery();
// class of the candidate instances
query.setClass(SimpleObjForQuery.class);
// either a collection or an extent
Extent extent = pm.getExtent(SimpleObjForQuery.class, true);
query.setCandidates(extent);

// demonstrate some queries


demonstrateFilterOnStringValue(query);
demonstrateFilterOnIntValue(query);
demonstrateFilterWithBooleanAnd(query);
demonstrateFilterWithNullTest(query);
demonstrateFilterWithGTOperator(query);
demonstrateFilterWithStartsWith(query);
demonstrateFilterWithEndsWith(query);
demonstrateSortOrder(query);

pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}

61
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

catch (Exception e)
{
// log this exception
}
pm.close();
}
}
This is just a utility method that prints out the results of the query.

private static void printResults(String whichTest, Object results)


{
System.out.println("***" + whichTest + "***");
Collection resultsCollection = (Collection) results;
for (Iterator iterator = resultsCollection.iterator();
iterator.hasNext();)
{
Object o = iterator.next();
System.out.println(o.toString());
}
}
Notice that the string constant uses double quotes like Java, and not single quotes like
SQL. Notice also that we use the == operator, rather than the equals() method.

private static void demonstrateFilterOnStringValue(Query query)


{
query.setFilter("theString == \"one\"");
printResults("filterOnStringValue", query.execute());
}
Native types (e.g. int) and wrapper types (e.g. java.lang.Integer) are treated the same.

private static void demonstrateFilterOnIntValue(Query query)


{
query.setFilter("theInt == 25");
printResults("filterOnIntValue", query.execute());
}
You can combine queries with && or || operators.

private static void demonstrateFilterWithBooleanAnd(Query query)


{
query.setFilter("theString == \"one\" && theInt == 25");
printResults("filterOnBooleanValue", query.execute());
}
Null tests in the filter expression are done in the same way as null tests in the Java
language. The runtime will translate this into a form appropriate for the database.
Although the JDO spec does not allow you to compare primitive types with null, the
MVCSoft runtime does support this.

62
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

private static void demonstrateFilterWithNullTest(Query query)


{
query.setFilter("theString == null");
printResults("filterWithNullTest", query.execute());
}
All the standard comparison operators are valid for dates, numbers, and strings. Type
promotions are automatic, and it is legal to compare wrapper types with primitive types.
You can also use the == and != operators with persistent objects.

private static void demonstrateFilterWithGTOperator(Query query)


{
query.setFilter("theInt > 25");
printResults("filterWithGTOperator", query.execute());
}
The startsWith and endsWith operators are called on the string object just like in Java.

private static void demonstrateFilterWithStartsWith(Query query)


{
query.setFilter("theString.startsWith(\"one\")");
printResults("filterStartsWith", query.execute());
}

private static void demonstrateFilterWithEndsWith(Query query)


{
query.setFilter("theString.endsWith(\"two\")");
printResults("filterEndsWith", query.execute());
}
The ordering expression is easy to understand. We set the filter to null to indicate we
want the entire extent.

private static void demonstrateSortOrder(Query query)


{
query.setFilter(null);
query.setOrdering("theInt descending, theString ascending");
printResults("sortOrder", query.execute());
}

}
Second Set: Some Simple Queries
This second example will demonstrate the following query techniques:
♦ the dot operator for navigating single-valued relationships
♦ using native types as parameters
♦ using wrapper types as parameters
♦ fully-qualified class names for parameters

63
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ class names qualified with imports for parameters


♦ class names imported by default for a parameter
♦ passing multiple parameters to the execute() method
♦ passing multiple parameters using an array
♦ passing multiple parameters using a map
An Additional Persistent Class : ParentObjForQuery
This class will be used in combination with the class used in the previous set of
examples. There is a relationship field in this class—SimpleObjForQuery
theSimpleObj—that should be configured as a relationship.
package com.mvcsoft.jdosamples.queries.simple_where;

import java.util.Date;

public class ParentObjForQuery


{
private String theString;
private Date theDate;
private SimpleObjForQuery theSimpleObj;

public ParentObjForQuery()
{
}

public ParentObjForQuery(String theString, Date theDate,


SimpleObjForQuery theSimpleObj)
{
this.theString = theString;
this.theDate = theDate;
this.theSimpleObj = theSimpleObj;
}

public String getTheString()


{
return theString;
}

public void setTheString(String theString)


{
this.theString = theString;
}

public Date getTheDate()


{
return theDate;
}

public void setTheDate(Date theDate)


{

64
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

this.theDate = theDate;
}

public SimpleObjForQuery getTheSimpleObj()


{
return theSimpleObj;
}

public void setTheSimpleObj(SimpleObjForQuery theSimpleObj)


{
this.theSimpleObj = theSimpleObj;
}

public String toString()


{
return "ParentObjForQuery: " + theString;
}
}
Populating Sample Values
This class just creates some additional sample values against which we can issue our
queries.
package com.mvcsoft.jdosamples.queries.simple_where;

import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
import javax.jdo.JDOException;
import java.util.Properties;
import java.util.Calendar;

public class QueryPopulateValues2


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.queries.simple_where.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()

65
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

// we're going to use these as children


// in the one-one relationship
SimpleObjForQuery simpleObj1 = new SimpleObjForQuery("child1",
1);
SimpleObjForQuery simpleObj2 = new SimpleObjForQuery("child2",
2);

// these are going to be parent objects


ParentObjForQuery parentObj1 = new ParentObjForQuery();
parentObj1.setTheString("parent1");
ParentObjForQuery parentObj2 = new ParentObjForQuery();
parentObj2.setTheString("parent2");
ParentObjForQuery parentObj3 = new ParentObjForQuery();
parentObj3.setTheString("parent3");
// set a date value
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(1985, 11, 2, 10, 5, 30);
parentObj1.setTheDate(calendar.getTime());
// for two of the parent objects, set the child objects
parentObj1.setTheSimpleObj(simpleObj1);
parentObj2.setTheSimpleObj(simpleObj2);

pm.makePersistentAll(new Object[]{parentObj1, parentObj2,


parentObj3});

pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();

66
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

}
Class with Sample Queries
package com.mvcsoft.jdosamples.queries.simple_where;

import javax.jdo.*;
import java.util.*;

public class IssueDotNotationAndParamQueries


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.queries.simple_where.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

67
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Query query = pm.newQuery();


// class of the candidate instances
query.setClass(ParentObjForQuery.class);
// either a collection or an extent
Extent extent = pm.getExtent(ParentObjForQuery.class, true);
query.setCandidates(extent);

// demonstrate some queries


demonstrateDotNotation(query);
demonstrateIntegerParam1(query);
demonstrateIntegerParam2(query);
demonstrateDateParamFullClass(query);
demonstrateDateParamImportedClass(query);
demonstrateImplicitPackageImport(query);
demonstrateMultipleParamsMethod1(pm);
demonstrateMultipleParamsMethod2(pm);
demonstrateMultipleParamsMethod3(pm);

pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

private static void printResults(String whichTest, Object results)


{
System.out.println("***" + whichTest + "***");
Collection resultsCollection = (Collection) results;
for (Iterator iterator = resultsCollection.iterator();
iterator.hasNext();)
{
Object o = iterator.next();
System.out.println(o.toString());
}
}

68
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This query demonstrates navigating through the single-valued relationship using the dot
notation. It doesn’t matter if the field theSimpleObj is null; the filter will just evaluate to
false.

private static void demonstrateDotNotation(Query query)


{
query.setFilter("theSimpleObj.theInt == 1");
printResults("demonstrateDotNotation", query.execute());
}
This query demonstrates passing a parameter declared as type int.

private static void demonstrateIntegerParam1(Query query)


{
query.declareParameters("int theParam");
query.setFilter("theSimpleObj.theInt == theParam");
printResults("demonstrateIntegerParam1", query.execute(new
Integer(1)));
}
This query demonstrates passing a parameter declared as type Integer.

private static void demonstrateIntegerParam2(Query query)


{
query.declareParameters("Integer theParam");
query.setFilter("theSimpleObj.theInt == theParam");
printResults("demonstrateIntegerParam1", query.execute(new
Integer(1)));
}
This query demonstrates a parameter with a fully-qualified class name.

private static void demonstrateDateParamFullClass(Query query)


{
query.declareParameters("java.util.Date theParam");
query.setFilter("theDate == theParam");
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(1985, 11, 2, 10, 5, 30);
printResults("demonstrateDateParamFullClass",
query.execute(calendar.getTime()));
}
This query demonstrates a parameter with an imported class.

private static void demonstrateDateParamImportedClass(Query query)


{
query.declareImports("import java.util.Date;");
query.declareParameters("Date theParam");
query.setFilter("theDate == theParam");
Calendar calendar = Calendar.getInstance();
calendar.clear();

69
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

calendar.set(1985, 11, 2, 10, 5, 30);


printResults("demonstrateDateParamImportedClass",
query.execute(calendar.getTime()));
}
This query demonstrates how classes in the same package as the candidate class are
automatically imported. We first get an instance of SimpleObjForQuery to pass as a
parameter.

private static void demonstrateImplicitPackageImport(Query query)


{
// let's get an instance to pass as a parameter
PersistenceManager pm = query.getPersistenceManager();
Query paramQuery = pm.newQuery();
paramQuery.setClass(SimpleObjForQuery.class);
paramQuery.setCandidates(pm.getExtent(SimpleObjForQuery.class,
true));
paramQuery.setFilter("theInt == 1");
Iterator iter = ((Collection) paramQuery.execute()).iterator();
if (!iter.hasNext())
throw new JDOUserException("Expected data not found");
SimpleObjForQuery param = (SimpleObjForQuery) iter.next();

query.declareParameters("SimpleObjForQuery theParam");
query.setFilter("theSimpleObj == theParam");
printResults("demonstrateImplicitPackageImport",
query.execute(param));
}
This is a utility method that gets a query for us with a filter that takes two parameters.
We’re going to execute this in three different ways.

private static Query getMultipleParamsQuery(PersistenceManager pm)


{
// we'll use a query on SimpleObjForQuery, rather than
ParentObjForQuery
// to save some complexity in getting param values
Query query = pm.newQuery();
query.setClass(SimpleObjForQuery.class);
query.setCandidates(pm.getExtent(SimpleObjForQuery.class, true));
query.declareParameters("int iParam, String sParam");
query.setFilter("theInt == iParam && theString == sParam");
return query;
}
If we have three or fewer parameters, we can just pass them in the execute() method call.

private static void


demonstrateMultipleParamsMethod1(PersistenceManager pm)
{
Query query = getMultipleParamsQuery(pm);
printResults( "multipleParamsMethod1", query.execute(new
Integer(1), "child1"));

70
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

}
We can pass the parameters as an Object[]. The order is the same order as they are
declared.

private static void


demonstrateMultipleParamsMethod2(PersistenceManager pm)
{
Query query = getMultipleParamsQuery(pm);
printResults( "multipleParamsMethod1", query.executeWithArray(new
Object[]{new Integer(1), "child1"}));
}
We can pass the parameters in a map. The key to the map is the declared parameter name,
and the value is the parameter value.

private static void


demonstrateMultipleParamsMethod3(PersistenceManager pm)
{
Query query = getMultipleParamsQuery(pm);
Map map = new HashMap();
map.put("iParam", new Integer(1));
map.put("sParam", "child1");
printResults( "multipleParamsMethod1", query.executeWithMap(map));
}
}
Third Set: Some Simple Collection Queries
This third example will demonstrate the following query techniques:
♦ navigation of many-valued relationships
♦ the isEmpty() method on collections
♦ querying on a collection, rather than an extent
Two Additional Persistent Classes : RelParentForQuery and RelChildForQuery
These two classes each have a many-valued relationship. RelParentForQuery has a field
children, which should be configured as a relationship with a target type of
RelChildForQuery.
package com.mvcsoft.jdosamples.queries.simple_where;

import java.util.Collection;

public class RelParentForQuery


{
private String someParentValue;
private Collection children;

public RelParentForQuery()
{

71
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public RelParentForQuery(String someParentValue, Collection children)


{
this.someParentValue = someParentValue;
this.children = children;
}

public String getSomeParentValue()


{
return someParentValue;
}

public void setSomeParentValue(String someParentValue)


{
this.someParentValue = someParentValue;
}

public Collection getChildren()


{
return children;
}

public void setChildren(Collection children)


{
this.children = children;
}

public String toString()


{
return "RelParentForQuery: " + someParentValue;
}
}
RelChildForQuery has a field simpleObjects, which should be configured as a
relationship with a target type of SimpleObjForQuery.
package com.mvcsoft.jdosamples.queries.simple_where;

import java.util.Collection;

public class RelChildForQuery


{
private String someChildValue;
private Collection simpleObjects;

public RelChildForQuery()
{
}

public RelChildForQuery(String someChildValue, Collection


simpleObjects)
{
this.someChildValue = someChildValue;

72
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

this.simpleObjects = simpleObjects;
}

public String getSomeChildValue()


{
return someChildValue;
}

public void setSomeChildValue(String someChildValue)


{
this.someChildValue = someChildValue;
}

public Collection getSimpleObjects()


{
return simpleObjects;
}

public void setSimpleObjects(Collection simpleObjects)


{
this.simpleObjects = simpleObjects;
}

public String toString()


{
return "RelChildForQuery: " + someChildValue;
}
}
Populating Sample Values
This class just creates some additional sample values against which we can issue our
queries.
package com.mvcsoft.jdosamples.queries.simple_where;

import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
import javax.jdo.JDOException;
import java.util.Properties;
import java.util.ArrayList;

public class QueryPopulateValues3


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.queries.simple_where.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");

73
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

// create our objects...


RelParentForQuery parent1 = new RelParentForQuery("parent1", new
ArrayList());
RelParentForQuery parent2 = new RelParentForQuery("parent2",
null);
RelChildForQuery child1 = new RelChildForQuery("child1", new
ArrayList());
RelChildForQuery child2 = new RelChildForQuery("child2", null);
SimpleObjForQuery grandchild1 = new
SimpleObjForQuery("grandchild1", 1);
SimpleObjForQuery grandchild2 = new
SimpleObjForQuery("grandchild1", 2);

// ...and put them in a hierarchy


parent1.getChildren().add(child1);
parent1.getChildren().add(child2);
child1.getSimpleObjects().add(grandchild1);
child1.getSimpleObjects().add(grandchild2);

// now make them persistent


pm.makePersistentAll(new Object[]{parent1, parent2});

pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();

74
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

}
Class with Sample Queries
package com.mvcsoft.jdosamples.queries.simple_where;

import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Iterator;

public class IssueCollectionQueries


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.queries.simple_where.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

75
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

Query query = pm.newQuery();


// class of the candidate instances
query.setClass(RelParentForQuery.class);
// either a collection or an extent
Extent extent = pm.getExtent(RelParentForQuery.class, true);
query.setCandidates(extent);

// demonstrate some queries


demonstrateNavigationOfManyValuedRelationships(query);
demonstrateCollectionEmptyTest(query);
demonstrateQueryOnCollection(query);

pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

private static void printResults(String whichTest, Object results)


{
System.out.println("***" + whichTest + "***");
Collection resultsCollection = (Collection) results;
for (Iterator iterator = resultsCollection.iterator();
iterator.hasNext();)
{
Object o = iterator.next();
System.out.println(o.toString());
}
}

76
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This is an example of navigating a many-valued relationship.

private static void


demonstrateNavigationOfManyValuedRelationships(Query query)
{
query.declareVariables("RelChildForQuery relChild;
SimpleObjForQuery simpleObj");
query.setFilter("children.contains(relChild) &&
relChild.simpleObjects.contains(simpleObj) && simpleObj.theInt==1");
printResults("navigateManyValued", query.execute());
}
This query demonstrates the use of the isEmpty method on a collection.

private static void demonstrateCollectionEmptyTest(Query query)


{
query.setFilter("children.isEmpty()");
printResults("emptyTest", query.execute());
}
This query demonstrates a query on a collection of objects, rather than an extent. We get
the collection from another query in this example, but the collection could come from
anywhere.

private static void demonstrateQueryOnCollection(Query query)


{
// with no filter, we'll get all the parent objects
// but this would work with a subset of objects too
query.setFilter(null);
Collection results = (Collection) query.execute();
// we'll use this rather than the extent
query.setCandidates(results);
query.setFilter("someParentValue==\"parent2\"");
printResults("queryOnCollection", query.execute());
}

}
Fourth Set: User-Defined Functions
This fourth example will demonstrate MVCSoft JDO’s ability to understand user-defined
functions in the query. As mentioned earlier, the JDO specification defines only three
methods for its query filters: startsWith(), endsWith(), and isEmpty() for collections only.
This is a “specification version 1.0” syndrome; obviously this is an inadequate list of
methods. Rather than define a limited set of additional methods, the MVCSoft JDO
toolkit allows you to define your own methods and their translation into SQL.
Methods are defined in a properties file. The format is simple. Fundamentally, it is a
series of entries where the method specification = sql translation conversion is specified.
The method specification is the fully qualified Java class name, a period, the method
name, and the number of parameters in parentheses. The sql translation is a literal string

77
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

where the parameters of the method call can be “copied in” using question marks with the
1-based index of their position in the method call. The ?0 “parameter” is the object on
which the method is called.
This will be clearer with a few examples. The following specifies the SapDB method
translation for a “length” method with no parameters for java.lang.String instances that
can be used in a filter like “theString.length() == 25.”
java.lang.String.length(0)=length(?0)

The following specifies the SapDB method translation for an “isEmpty” method with no
parameters for java.lang.String instances that can be used in a filter like
“theString.isEmpty().”
java.lang.String.isEmpty(0)=length(?0)\=0
The following specifies the SapDB method translation for the standard JDO “startsWith”
method, which takes one parameter for java.lang.String instances and can be used in a
filter like “theString.startsWith(“abc”).”
java.lang.String.startsWith(1)=?0 like (?1||'%')
The following specifies the SapDB method translation for the standard JDO “endsWith”
method, which takes one parameter for java.lang.String instances and can be used in a
filter like “theString.endsWith(“abc”).”
java.lang.String.endsWith(1)=?0 like ('%'||?1)
The following specifies the SapDB method translation for a “sqrt” method with no
parameters for java.lang.Integer instances that can be used in a filter like “theInt.sqrt() <
25.”
java.lang.Integer.sqrt(0)=sqrt(?0)
Of course, it would be very annoying to need to define a sqrt method for
java.lang.Integer, java.lang.Long, java.lang.Short, java.lang.Byte, etc. It would also be
annoying not to be able to make function calls on primitive types. So for all the following
classes—java.lang.Integer, int, java.lang.Short, short, java.lang.Byte, byte,
java.lang.Long, long, and java.math.BigInteger—you should define your method on
java.lang.Integer. It will work for any of the other listed types. And for all the following
classes—java.lang.Float, float, java.lang.Double, double, and java.math.BigDecimal—
you should define your method on java.lang.Float. It will also work for any of the other
listed types.
UserDefinedFunctions Properties File
This is the properties file that we’ll use for the next example. It should be available on the
classpath at
com/mvcsoft/jdosamples/queries/simple_where/UserDefinedFunctions.properties. (You
should modify the SQL translations to be appropriate for your target database.)
java.lang.String.length(0)=length(?0)

78
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

java.lang.String.isEmpty(0)=length(?0)\=0
java.lang.String.startsWith(1)=?0 like (?1||'%')
java.lang.String.endsWith(1)=?0 like ('%'||?1)
java.lang.Integer.sqrt(0)=sqrt(?0)
Class with SampleQueries
package com.mvcsoft.jdosamples.queries.simple_where;

import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Iterator;

public class IssueUserDefinedFunctionQueries


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.queries.simple_where.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getPersistenceManager(Properties


properties)
{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(properties);

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
Properties properties = getStandardProperties();
Here we tell the PersistenceManagerFactory where it can find our properties file with the
user-defined functions.

79
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

properties.setProperty("com.mvcsoft.jdo.FunctionMap",
"com/mvcsoft/jdosamples/queries/simple_where/UserDefinedFunctions.prope
rties");
pm = getPersistenceManager(properties);
pm.currentTransaction().begin();

Query query = pm.newQuery();


// class of the candidate instances
query.setClass(SimpleObjForQuery.class);
// either a collection or an extent
Extent extent = pm.getExtent(SimpleObjForQuery.class, true);
query.setCandidates(extent);

// demonstrate some queries


demonstrateFilterUDFLength(query);
demonstrateFilterUDFMax(query);

pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

private static void printResults(String whichTest, Object results)


{
System.out.println("***" + whichTest + "***");
Collection resultsCollection = (Collection) results;
for (Iterator iterator = resultsCollection.iterator();
iterator.hasNext();)
{
Object o = iterator.next();
System.out.println(o.toString());
}
}
Here is a query filter using the sqrt function we defined in the properties file.

private static void demonstrateFilterUDFMax(Query query)

80
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
query.setFilter("theInt.sqrt() >= 2");
printResults("filterSqrt", query.execute());
}
Here is a filter using the length function we defined in the properties file.

private static void demonstrateFilterUDFLength(Query query)


{
query.setFilter("theString.length() == 7");
printResults("filterLength", query.execute());
}

81
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

SQL-Based Querying with Hyperquery


The JDO specification anticipates that other query languages besides JDO-QL will be
provided by the vendor. A newQuery method on the PersistenceManager interface allows
for the specification of the query language:
Query newQuery (String language, Object query);
The language parameter for JDO-QL is javax.jdo.query.JDOQL (although typically, you
will use one of the other newQuery methods where this is the default). MVCSoft
provides an SQL-based query language known as Hyperquery. The language parameter
for retrieving a Hyperquery query instance is com.mvcsoft.query.Hyperquery, although
you can use the predefined constant
com.mvcsoft.jdo.runtime.user.MVCSoftQuery.HYPERQUERY instead.
The Hyperquery query language provides the same type of flexibility as SQL select
statements, while maintaining awareness of Java language constructs such as inheritance
and associations between classes. It includes support for:
♦ Subqueries
♦ Group by clauses, having clauses, and aggregate functions (min, max, avg, sum,
count)
♦ Various join types (left join, right join, inner join)
♦ SQL functions (upper, lower, substring, trim, abs, etc.)
♦ Select clauses for multiple objects, individual fields, etc

The Hyperquery syntax conforms, where possible, to Hibernate’s query


language to facilitate porting from Hibernate to MVCSoft, and vice versa.
Other query language syntaxes will be provided in future versions of
MVCSoft based on customer demand.

There are two remaining sections in this chapter. One explains usage of the
javax.jdo.Query interface when it represents a Hyperquery query, and the other describes
the Hyperquery filter syntax.
Usage
The javax.jdo.Query interface is used for both JDO-QL and Hyperquery queries. A
simple use of a Hyperquery query might look like this:
Query query = pm.newQuery(MVCSoftQuery.HYPERQUERY, null);
query.declareImports(
"import com.mvcsoft.jdosamples.queries.hyperquery.*");
query.setFilter("from Zebra");
Collection results = (Collection) query.execute();

82
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

A Hyperquery query, like an SQL query but unlike a JDO-QL query, has a from clause
that specifies the data over which the query operates. Because of this, the setClass and
setCandidates methods on the javax.jdo.Query interface are no-ops. Also, because you do
not associate a javax.jdo.Extent instance with a Hyperquery, query subsets need to be
specified directly on the query instance by casting it to an instance of
com.mvcsoft.jdo.runtime.user.MVCSoftSubsettable. See below for an example of this.
One of two techniques are available to pass parameters to a Hyperquery instance: ordered
question marks like JDBC with SQL, or named parameters passed in a java.util.Map
instance. Because parameters aren’t passed using JDO-QL parameter declarations, the
declareParameters method on the javax.jdo.Query interface is a no-op. See below for
examples of using parameters with a Hyperquery query.
Hyperquery allows you to specify the classes and properties to be returned from the
query. If there is more than one class specified in the select clause, multiple fault groups
can be associated to the query based on the alias given to that class. See below for
examples of this.
Parameters
Parameters are declared in the query filter and passed in one of the Query.execute
methods. The most straightforward way to declare parameters is to give them names
(identifiers prepended with a “:”), and to give values to those names in a java.util.Map
instance passed to the Query.executeWithMap method. For instance:
query.declareImports(
"import com.mvcsoft.jdosamples.queries.hyperquery.*");
query.setFilter("from Zebra z where z.name=:sound or z.noise=:sound");
Map map = new HashMap();
map.put("sound", "moo");
Collection results = (Collection) query.executeWithMap(map);
You can also use the JDBC method of specifying parameters by position using a question
mark placeholder. These parameters are automatically filled in based on the matching
order of parameters passed to one of the Query.execute methods. For instance:
query.declareImports(
"import com.mvcsoft.jdosamples.queries.hyperquery.*");
query.setFilter("from Zebra z where z.name=? or z.noise=?");
printResults("paramsInOrder", query.execute("moo", "moo"));
Notice that “moo” had to be passed twice to the query.execute() method because it was
used twice.
Fault Groups
Because a JDO-QL query—at least in JDO 1.01—has a single return type (the “candidate
class”), the fault group can be specified using the following method signature:
MVCSoftQuery.setFaultGroup(UserFaultGroup faultGroup)

83
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

You can still use this method if there is only one class type being returned. For example,
the following code sets the fault group in the same way that it would if the query were a
JDO-QL query:
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getMyProperties());

PersistenceManager pm = factory.getPersistenceManager();

Query query = pm.newQuery(MVCSoftQuery.HYPERQUERY, null);

query.declareImports(
"import com.mvcsoft.jdosamples.queries.hyperquery.*");

MVCSoftQuery mvcq = (MVCSoftQuery) query;

MVCSoftPersistenceManagerFactory mvcPMF =
(MVCSoftPersistenceManagerFactory) factory;
UserFaultGroup zebraFaultGroup =
mvcPMF.getFaultGroup(Zebra.class.getName(), "zebraFaultGroup");

// single return element


query.setFilter("from Zebra z");
map = new HashMap();
map.put("nameZebra", "zebra1");
mvcq.setFaultGroup(zebraFaultGroup);
Collection results = (Collection) query.executeWithMap(map);
However, if you have multiple return elements declared in your query filter, the
MVCSoft runtime can no longer infer which fault group should be assigned to which
return element. That’s when you use the alias of the return element as a key with which to
associate the fault group:
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getMyProperties());

PersistenceManager pm = factory.getPersistenceManager();

Query query = pm.newQuery(MVCSoftQuery.HYPERQUERY, null);

query.declareImports(
"import com.mvcsoft.jdosamples.queries.hyperquery.*");

MVCSoftQuery mvcq = (MVCSoftQuery) query;

MVCSoftPersistenceManagerFactory mvcPMF =
(MVCSoftPersistenceManagerFactory) factory;
UserFaultGroup zebraFaultGroup =
mvcPMF.getFaultGroup(Zebra.class.getName(), "zebraFaultGroup");
UserFaultGroup antelopeFaultGroup =
factory.getFaultGroup(Antelope.class.getName(),
"antelopeFaultGroup");

// multiple return elements

84
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

query.setFilter(
"from Zebra z, Antelope a where z.name=:nameZebra and
a.name=:nameAntelope");
Map map = new HashMap();
map.put("nameZebra", "zebra1");
map.put("nameAntelope", "antelope1");
mvcq.associateFaultGroup("z", zebraFaultGroup);
mvcq.associateFaultGroup("a", antelopeFaultGroup);
Collection results = (Collection) query.executeWithMap(map);
Query Subsets
Rather than setting the subsetting technique on the extent, as you do for a JDO-QL query,
you specify it directly on the query by casting the query instance to
com.mvcsoft.jdo.runtime.user.MVCSoftSubsettable. Everything else is the same:
query.declareImports(
"import com.mvcsoft.jdosamples.queries.hyperquery.*");
query.setFilter(
"from Zebra z where substr(z.name, 1, 10)='multizebra' order by
z.name");

MVCSoftSubsettable mvcsubsettable = (MVCSoftSubsettable) query;


mvcsubsettable.setSubsetTechnique(
MVCSoftSubsettable.SUBSET_ITERATE_RESULTS, 5);

printResults("paramsInOrder", query.execute());

Query Syntax
If you are familiar with SQL, you already know how to write Hyperquery queries. A
Hyperquery query:
♦ lets you query using Java class and field names. You don’t need to know anything
about the underlying database schema
♦ looks like an SQL select statement, with the familiar clauses select, from, where,
group by, having, and order by
♦ understands Java collection classes (maps, sets, and lists) and how they represent
relationships
♦ works like an SQL select statement, with the familiar table-oriented set logic
♦ returns JDO classes, individual properties (represented as numbers, strings, dates,
etc), or even new Java class instances
♦ even allows sub-queries in the where clause

The Parts of a Query


A Hyperquery query has the following parts:

85
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

select clause (optional)


Your query can return a collection of JDO class types specified in your from clause, by
selecting the alias of that from clause declaration. For instance: select cust from Customer
cust. It can return a collection of simple property types by navigating to this type from n
alias, e.g. select cust.name from Customer cust. It can return a collection of Java value
object classes by using the new operator, e.g. select new
com.mycompany.MyCustomerView(cust.name, cust.city, cust.phoneNum) from Customer
cust. It can return aggregate information by using the functions count, sum, max, min,
and avg, e.g. select avg(cust.account) from Customer cust. It can return the contents of a
relationship collection using the special function elements, e.g. select
elements(cust.contacts) from Customer cust. It can even return multiple results in a
single query. In this case, the collection contains Object[] instances, and the contents of
the array correspond to the contents of the select clause, e.g. select cust.account,
cust.name, cust from Customer cust will return a collection of arrays, and each array will
have three elements. The first element will be the account, the second will be the name,
and the third will be a Customer instance.
If there is no select clause, the runtime will infer one from the from clause.
from clause (mandatory)
This is the only mandatory clause. Although you are specifying Java classes and
relationships in the from clause, the semantics are those of the familiar SQL select
statement. Each item in the from clause can be thought of as a table. When you specify
multiple classes in the from clause, they are combined just like the equivalent SQL tables
would be combined. For instance, the Hyperquery query from Customer cust, Order ord
will return a collection of arrays providing you with each and every combination of
Customer and Order. (This particular query has little utility, but it’s important to
understand the theory.)
The primary difference from SQL is that the from clause in a Hyperquery query
understands how to navigate your JDO relationships (single-valued and multi-valued).
You can use outer join, left join, or right join semantics to do this. For instance, the query
from Customer cust join cust.orders ord join ord.lineItems lineItem will return only those
customers who have at least one order with at least one line item. The query from
Customer cust left join cust.orders ord left join ord.lineItems lineItem will return all
customers regardless of whether or not a customer has orders, or an order has line items.

Hibernate’s query language has the concept of a “fetch join,” which allows
you to specify relationships that should be eagerly loaded. The MVCSoft
JDO Toolkit has the more flexible and powerful concept of fault groups
instead. MVCSoft fault groups (1) allow more than one collection-valued
relationship to be eagerly loaded; (2) allow fine-grained control over the
fields to be loaded; and (3) are orthogonal to the query, which allows you
to “mix and match” query design and data loading requirements.

86
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

where clause (optional)


The where clause operates just as it does in an SQL select statement, by testing rows for
inclusion in the result set. The primary difference is that the tests you write will be on
class fields, rather than table columns. You can compare columns to constants or other
columns using the standard SQL operators <,>, <>, =, >=, and <=, e.g. from Customer
cust where cust.name=’Johnson’. You can join tests together using and and or, or
negate these tests using not, e.g. from Customer cust where cust.salesAmount > 1000 or
cust.creditLimit > 10000. You can test for null using is null or is not null, e.g. from
Customer cust where cust.discountCode is not null. You can pass parameters either by
name, e.g. from Customer cust where cust.name=:name, or by position, e.g. from
Customer cust where cust.name=?. You can navigate through single-valued
relationships using the dot operator, e.g. from Customer cust where
cust.salesRep.name=’Bill’. You can use sub-queries in the where clause, in the same
way you would with an SQL statement, using any, all, or some, e.g. from Customer cust
where cust.salesRep = any (select rep.name from SalesRep rep where rep.status=’fired’),
exists or not exists, e.g. from Customer cust where exists(from SalesRep rep where
rep.topProspect = cust), in or not in, e.g. from Customer cust where cust.salesRep = any
(select rep.name from SalesRep rep where rep.status=’fired’), or an aggregate function
call, e.g. from Customer cust where cust.salesAmount > (select avg(c.salesAmount) from
Customer c).
The underlying idea is to allow you to write the same query you would if you were
writing SQL…only to have the advantage of writing these queries on your objects, rather
than your database schema. As well as the above, you can use string concatenation (||),
SQL functions (whichever are supported by your database, like upper, charlength,
position, round, mod…), the in operator, the between operator, and so forth.
There are also some Hyperquery-specific features that add to the syntax and semantics
imported from SQL. For more details see the section Hyperquery-Specific Extensions,
below.
group by clause (optional)
The group by clause operates as it does in SQL select statements, for queries that return
aggregate values. The only difference is that you group on class fields rather than
database columns.
having clause (optional)
The having clause operates as it does in SQL select statements, for queries with a group
by clause.
order by clause (optional)
The order by clause operates as it does in SQL select statements. The only difference is
that you order by class fields rather than database columns. You may optionally specify
an asc or desc to indicate the sort order.

87
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Hyperquery-Specific Extensions
There are six Hyperquery-specific extensions to help enable you to write queries that take
full advantage of the Java context in which you are operating. (All of these extensions are
compatible with Hibernate queries.) They are:
♦ Array syntax to access individual collection or map elements. If you have a
java.util.Map, an array, or any collection class that maintains an index, you can
specify a particular element for a map as follows; for a map: from SalesRep rep where
rep.mapProductToSales[‘extruded plastic’] > 100000, or for an array or collection:
from SalesRep rep where rep.territoryHistory[0]=’Buffalo’
♦ Functions to get the largest or smallest index or element in a map, array or collection,
or the size of the collection. (These functions can also be used as properties of the
collection.) The available functions are size, maxIndex, minIndex, maxElement, and
minElement. The size function or property returns the size of the collection, e.g. from
SalesRep rep where rep.customers.size > 5 (or from SalesRep rep where
size(rep.customers) > 5). The maxIndex and minIndex functions or properties return
the largest or smallest indexes into the collection, e.g. from SalesRep rep where
rep.territoryHistory[maxIndex(rep.territoryHistory)]=’Albany’. The maxElement
and minElement functions or properties return the largest or smallest values in the
collection, e.g. from SalesRep rep where rep.salesHistory.minElement > 100000.
♦ Functions to refer to the values in a collection or map (or keys in a map) in the
aggregate. The two available functions are elements (for values) and indices (for
keys). In any construct type in which you would use a subquery—i.e. tests for any,
all, some, exists, or in—you can also syntactically use an elements or an indices test.
For example, from SalesRep rep where 100000 > all elements(rep.salesHistory)
♦ .class syntax to test against the class type (i.e. the discriminator column), e.g. from
Customer cust where cust.class = com.mycompany.CorporateCustomer
♦ .id syntax to access the object identifier, e.g. from Customer cust where
cust.id=’12AA3498’. This is unnecessary in the case of application identity, where
you can just access the key field or fields directly, but is useful in the case of
datastore identity, where there is no specific class field associated with the key. In the
case of application identity with multiple key fields, you must specify them
individually, e.g. from Customer cust where cust.id.fName=’Bob’ and
cust.id.lName=’Smith’.
♦ Java public static final constants in queries, e.g. from Customer cust where
cust.creditRating = com.mycompany.CreditRatings.GOLD

88
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

The MVCSoft JDO GUI


The MVCSoft JDO graphical user interface is a versatile tool that can be used to enhance
developer productivity during the JDO application development process. The GUI is a
simple and intuitive way to configure your JDO application. The following tasks can be
managed from the GUI:
♦ indicating which classes should be persistence capable and persistence aware
♦ creating JDO standard metadata
♦ configuring database mappings, key factories, and runtime behavior for an application
♦ running the MVCSoft JDO enhancer
♦ generating and compiling MVCSoft descriptor classes
♦ creating and removing database tables required by your application

Many developers will want to set up a productive work environment that


generates metadata and performs the required JDO enhancement and
generation steps without using a GUI. Fortunately, the MVCSoft JDO
Toolkit accomodates these developers as well. Every task in the GUI can
also be performed by an MVCSoft Ant or XDoclet task.

Preparing and Running the GUI


MVCSoft does not redistribute tools.jar, so you must copy it from your Java installation
to the lib directory of the MVCSoft JDO installation directory. Make sure that the
tools.jar file comes from the same version of Java that you will be using to run the
MVCSoft JDO GUI.
You can run the GUI by typing java –jar mvcjdoall.jar from the root folder of the
MVCSoft JDO installation directory. On some operating systems (such as Microsoft
Windows), it is often possible to start a jar-based application by double-clicking it in the
file manager.
Saving Properties Between Invocations
By default, the MVCSoft JDO GUI saves configuration information in a file named
jdotool.properties, in the same directory as the mvcjdoall.jar JAR. You can specify a
different file by setting the Java property com.mvcsoft.jdo.gui.propertiesfile. If the file
does not exist when the GUI is opened, an error message will display when the GUI is
opened, but the file will be created.
The Screen Areas and Their Use

89
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

The JDO GUI screen is divided into three areas: the tree pane, the messages pane, and
the work pane.
The Tree Pane
The tree pane is on the leftmost side of the screen. You can control what is shown in this
pane using the filter combobox at the top. There are five filters: All Objects; Persistence
Capable; Capable and Aware; Fault Groups; and Key Generators. The first three filters
(All Objects, Persistence Capable, and Capable and Aware) give you various views of the
Java classes available from the class sources you have defined in your project. The fourth
filter (Fault Groups) gives you a list of fault groups (aka load policies) that you have
defined. The fifth filter (Key Generators) gives you a list of generators you have created
for producing datastore primary keys.
All Objects Filter
The All Objects filter is primarily used to add new persistence-capable or –aware classes
to your project. It will give you a folder-and-classes view of your class sources, and it
will include all classes (regardless of whether they are persistence capable, persistence
aware, or plain Java classes). To change the status of a class, navigate to it and then right
click the class (or select multiple classes and right click the selection). Choose the new
status from the pop-up menu.
Persistence Capable Filter
The Persistence Capable filter is the filter you will use the most. It gives you a flat list
of all your persistence-capable classes. These are the classes that may require further
configuration, so it is convenient to have all of them in a flat list.
To change a class-level configuration setting, click on the class and change the value in
the appropriate tab in the work pane. To change a field-level configuration setting,
navigate to the field in the tree view and click on it. Change the value in the appropriate
tab in the work pane.
Capable and Aware Filter
The Capable and Aware filter is similar to the Persistence Capable filter, but it adds the
persistence aware classes to the list. This is most useful for reviewing which classes have
been made persistence aware, since persistence aware classes do not require further
configuration.
Fault Groups Filter
The Fault Groups filter is used to display the base classes for which you might define
static fault groups (aka loading policies). You can add, remove, and define fault groups
for the selected base class in the work pane.
Key Generators Filter

90
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

The Key Generators filter is used to give you a list of generators you have created for
producing datastore primary keys. To add a key generator to the list, select the menu item
Add Key Factory… on the Edit top-level menu. (You must fill in all the values in the
“Create a New Key Factory” dialog.) To further configure the key factory, click on the
item in the tree view and change the values in the work pane.

A key generator is just a factory for primary key values. There are two
highly-configurable algorithms used by the MVCSoft JDO runtime. One
produces values using a “universally unique identifier” scheme based
on—at your discretion—bytes from a timestamp, an ip address, a system
hash value, a start time, and a counter. The other retrieves a block of IDs
from a database table and hands them out until they are exhaused, at which
point another block is retrieved. The block size, retry count, table name,
sequence name, and sequence and increment column names and types can
all be configured.
You can set the appropriate key generator when you configure that
persistence-capable class.

The Messages Pane


The messages pane is the bottom panel of the GUI. It is used to print out messages to the
user about current operations. Stack traces are also redirected to this message pane. You
can cut or copy text from this message pane, e.g. for inclusion in e-mail messages to
MVCSoft newsgroups.
The Work Pane
The work pane is the central panel of the GUI. Various controls (sometimes in tabbed
panes) will appear here depending on what element is selected in the Tree Pane. There
are three different sets of controls that can appear, depending on your Tree Pane
selection: key factory configuration; class settings configuration; or field settings
configuration. This is where the bulk of the configuration work is done, so each of these
configuration screen groupings has a section later in this chapter.
The Concept of a Project
In the MVCSoft GUI, a project encompasses a list of persistence capable and aware
classes, and all the settings needed to enhance these classes (or make them persistence
aware) and generate the metadata they need at runtime. Note that a project is a GUI IDE
concept only. If you are developing with Ant tasks and/or XDoclet, you don’t need to
know about projects.
Along with the various metadata .jdo files that the GUI can produce, a project has three
“controlling” files: an XML file that has the project-level settings; a text file with a list
of persistence capable classes; a text file with a list of persistence aware classes. It’s
the project-level settings file that you open when you choose File/Open Project… from

91
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

the menu, and for which you choose a name when you choose File/Save Project… or
File/Save Project As….
For the most part, you can ignore the existence of the two text files with lists of
persistence capable and aware classes. The GUI assigns names to the persistence capable
class listing and the persistence aware class listing based on the name of the project-level
settings file. By default they are stored in the same directory as the project .xml file. You
can rename them in the Configure Project Settings dialog box, on the Build Config page
of the tab.

The persistence capable and aware class listings are stored apart from the
project settings because you may want to generate or use these lists apart
from the global settings for the project.

Class Sources
The MVCSoft GUI introduces a concept of class sources. Every project needs at least
one class source, which is just a “source” JAR or directory tree. A class source might be
where some of your project’s .class sources are stored; these class sources appear as top-
level folders in the tree pane when you select the “All Objects” filter. A class source can
also be a “library,” on which some of your project’s classes depend. Class sources you
mark as a library don’t appear in the tree pane.
Your project might have a single directory as its only class source, or it might have
multiple JARs and directories. Consider a typical scenario where your application has a
JAR corresponding to each functional area. You might have the following:
♦ purchasing.jar
♦ inventory_management.jar
♦ order_management.jar
♦ sales_forecasting.jar
Here, you would define four class sources, one for each JAR. You might need to define
additional class sources for dependencies, e.g. for
1) utilities.jar
2) servlet.jar
3) ejb.jar
In this environment, you would have a total of seven class sources. All seven of them
would be of type “jar.” (The other available type is “directory.”) You would provide the
JAR file-system name and a JAR description. The description is often used in other
configuration settings, and you should always provide one for non-library class sources.
Finally, you would mark the last three JARs as library JARs.

92
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Class sources can be defined using the New Project Wizard. They can also be added,
removed, or modified using the Configure Project Settings dialog, on the Class Sources
tab.

The EJB 2.0/2.1 model of container-managed persistence does not allow


you to have inter-JAR relationships. In other words, you can’t have a
relationship between an EJB in the purchasing JAR and an EJB in the
inventory management JAR. This is a major practical limitation of the
spec, requiring either that you put all your classes in one giant JAR—and
one deployment descriptor—or you manage some of your relationships
manually. The JDO spec has no such limitation, and the MVCSoft JDO
Toolkit allows relationships between objects in different JARs or
directories.

Metadata
The JDO spec requires that—for maximum portability—the XML data for a class be
stored in a file named either <class-name>.jdo (for a single class), or <package-
name>.jdo (for an entire package). The MVCSoft JDO GUI can generate metadata for
either option, and can also store all the metadata in a single file.
What are the advantages and disadvantages of each option? Storing all the metadata in
multiple files (either at the class or package level) makes it easier to make changes if you
have many programmers because there are fewer merge conflicts. Storing all the
metadata in a single file makes it easier to have multiple versions of the metadata.
The JDO spec also requires that—for maximum portability—the XML data for a class be
available as a resource loaded by the same class loader as the class. By default, the
MVCSoft JDO GUI cooperates in this requirement by storing metadata files in the class
sources from which the relevant .class files came. However, in many environments it
makes more sense to store the metadata with .java source files rather than with .class
binary files. The JDO GUI will allow you to specify a class source in which all metadata
should be stored, using the Target for .class Metadata setting in the Configure Project
Settings dialog. The typical use for this would be to set up a directory class source that
corresponds to your .java source tree directory, and use that class source as the metadata
target.
Relative File Names
Storing absolute file or directory names for class sources, persistence-capable and –aware
class listings, etc. can lead to problems in multi-developer environments. For instance,
one developer may check out the project to a C: drive and another might check out the
project to a D: drive, or even a different sub-directory from the original project creator.
To solve this problem, the MVCSoft JDO GUI will use relative file names whenever the
file is in the same directory or subdirectory as the project xml file.

93
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Some projects may not want to store the project xml file at the root directory, but will still
want file or directory names stored as relative paths. In this situation, you can provide a
value for the setting Project Base Relative to Settings File. It will usually just be a series
of navigations to parent directories, e.g. “/..” or “/../..”. If this value is specified, all file
and directory names will be relative to this directory.
MVCSoft Metadata Class
The MVCSoft JDO runtime requires access to a generated .class file that contains all the
metadata for persistence-capable classes. The MVCSoft JDO GUI can generate the .java
file, compile it, and place it in one of the class sources (i.e. in one of your JARs or
directories) so that it is available to the runtime.
You must specify the class source to which the MVCSoft JDO GUI can add the
generated .class file. You can do this when using the New Project Wizard, or by setting
the Target for .class Metadata property in the Configure Project Settings dialog. You
should also specify the package and class name, so that you can pass it as a property to
the PersistenceManagerFactory.
You can specify the output directory for the generated .java source if you would like to
have this source file available during debugging, using the Generated Source Output
Directory setting in the Configure Project Settings dialog. (Otherwise it will be generated
to a temp directory.)
Deployment Options
Enhanced classes generated metadata classes can be placed directly in the class sources
you defined for this project. This is the “Enhance in Place” option for the Enhance in
Place setting of the Configure Project Settings dialog. However, if you would prefer, you
can also have copies of these JARs and directories made for the enhanced classes and
generated metadata. This is the “Enhance to Directory” option for the Enhance in Place
setting of the Configure Project Settings dialog. If you choose the “Enhance to Directory”
option, you should also specify a value for the Enhanced Bytecode Output Directory,
which is the target directory when metadata is generated or class files are enhanced.
Installed Data and Constant Transformers
The MVCSoft JDO Toolkit allows the user to specify classes that provide
transformations of data types between the object state (or query constant) and the
database. Listing them in the project allows them to be selected from various
comboboxes in the GUI.
Menu Item Reference:
There are seven top level menus: File, Edit, View, Deploy, Database, Options, and Help.
File
The File top-level menu has nine menu items: New Project Wizard (Simple), New Project
Wizard…, New Project, Open Project, Save Project, Save Project As…, Regenerate .jdo

94
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Metadata Only, Save Project .xml File Only, and Exit. It also has the MRU list for project
files.
New Project Wizard (Simple)…
This menu item displays the simplified New Project Wizard dialog box. There are three
configuration screens in this wizard:
1) Set Metadata Name—this asks you to specify the metadata package and class
name. You will need to provide these when you instantiate a JDO factory. This is
mandatory.
2) Set Target Database—this asks you to specify a configuration file that provides
default values for SQL types, JDBC types, and data transformation classes.
3) Set Directory for your .class and .jdo Files—this asks you to specify the directory
where your .class files are located and where the .jdo metadata files will be stored.
New Project Wizard…
This menu item displays the New Project Wizard dialog box. See the section
“Configuring a Project for MySimpleClass Using the New Project Wizard” for an
example of its use. There are eight configuration screens in this wizard:
1) Set Metadata Name—this asks you to specify the metadata package and class
name. You will need to provide these when you instantiate a JDO factory. This is
mandatory.
2) Set Target Database—this asks you to specify a configuration file that provides
default values for SQL types, JDBC types, and data transformation classes.
3) Set .jdo Format—this asks you to specify the way in which you would like to
organize .jdo files: one per package, one per class, or one per project. (If one per
project, you must also specify the file name.) This is optional. The default is per-
package.
4) Add Directories and JARs for .class Files—this asks you to add one or more
sources for your application .class files. You must give each source a unique
descriptive name. This is mandatory.
5) Add Directories and JAR Libraries—this asks you to add any necessary
directories and JARs with classes on which your persistent application classes
depend. (Without these libraries, the .class files cannot be resolved.) This is not
necessary if you do not have these dependencies.
6) Set Target Directory or JAR for Metadata—this asks you to specify the JAR or
directory to which the compiled metadata class would be saved. This is
mandatory.
7) Set Source Directory for .jdo Files—this gives you an opportunity to save the .jdo
files with your source code rather than your bytecode. This is optional.

95
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

8) Set Deploy Directory for Metadata and Enhanced Classes—this gives you the
opportunity to have JDO bytecode enhancement and metadata class generation
take place on a copy of your bytecode, rather than the original. This is optional.
The default is to overwrite the existing bytecode (which is usually the most useful
option).
New Project
This creates a new project, discarding the existing project in the workspace. (If there are
unsaved changes in an existing project, you will be prompted to save them.) If you use
the New Project rather than New Project Wizard… menu item, you must then configure
the project using the Configure Project… menu item from the Options top-level menu.
Open Project
This opens an existing project, discarding the existing project in the workspace. (If there
are unsaved changes in an existing project, you will be prompted to save them.)
Save Project
This saves the open project, using the current project file name. (If this is a new project
without a name, this menu item operates as Save Project As…) Saving the project saves
the project xml file, and also saves the following information:
♦ An updated list of persistence-capable classes.
♦ An updated list of persistence-aware classes.
♦ The updated .jdo files for any classes where the metadata has been modified.
Save Project As…
This saves the open project, prompting the user for the name. If there is no file extension,
the extension .xml is automatically appended to the project file name.
Regenerate .jdo Metadata Only
This option will save all the .jdo files for all persistent classes, regardless of whether the
metadata has been modified.
Save Project .xml File Only
This option will save only the project xml file, without updating the lists of persistence-
capable and persistence-aware classes, and without writing any .jdo metadata files. The
primary use for this option is to fix problems with the project settings without the risk of
corrupting the other files.
Exit
This causes the MVCSoft GUI to exit, prompting the user to save the project if it is
modified.
MRU List

96
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

The four most recently used project files are listed. To reopen one of those files, select it
from the list.
Edit
The Edit top-level menu has six menu items: Add Key Factory…, Remove Selected Key
Factories…, Edit Relationship Fields…, Edit List of Managed Classes…, Add PC
Class…, and Add Aware Class…
Add Key Factory…
This menu item displays the Create a New Key Factory dialog box. This dialog box
asks for the Key Factory name, which is arbitrary but used in configuring datastore oids
for classes; the Key Factory Class, of which UUID and Blocks are available by default;
and Store Metadata with Class, which asks you to select a class in whose .jdo metadata
the key factory’s configuration will be stored. You can further configure the key factory
by selecting “Key Generators” from the tree pane combo box.
Remove Selected Key Factories…
This menu item deletes the key factories currently selected in the tree pane.
Edit Relationship Fields…
This menu item displays a dialog that allows the developer to edit all the relationship
fields in one place, rather than navigating from class to class.
Edit List of Managed Classes…
This menu item displays a dialog that lists all the .class files available from the (non-
library) project class sources. The classes can be set as persistence capable, persistence
aware, or unmanaged.
Add PC Class…
This menu item displays a dialog that lets you add a persistence capable class by typing
the simple class name (without the package).
Add Aware Class
This menu item displays a dialog that lets you add a persistence aware class by typing the
simple class name (without the package).
View
The View top-level menu has three menu items: Reload Classes…, Go To Class…, and
Go To Field…
Reload Classes…

97
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This menu item reloads the JARs or file systems you have defined as class sources. This
is useful if you are simultaneously using the MVCSoft GUI and developing the
application persistence class hierarchy.
Go To Class
This menu item displays a dialog that lets you navigate to a persistence capable class
without using the tree.
Go To Field
This menu item displays a dialog that lets you navigate to a field of a persistence capable
class without using the tree. You must already have navigated to a persistence capable
class in order for there to be a context for this dialog.
Deploy
The Deploy top-level menu has three menu items: Generate Descriptor, Generate and
Compile Descriptor, and Enhance Classes.
Generate Descriptor
This generates the .java file for the metadata class (which is the compiled form of the
metadata in the .jdo files). You might choose this, rather than Generate and Compile
Descriptor, if you were going to compile the metadata class yourself.
Generate and Compile Descriptor
This generates the .java file for the metadata class, compiles it into a .class file, and
inserts it into the JAR or directory selected as the “target for .class metadata.” If the
project option is “Enhance to Directory,” rather than “Enhance in Place,” a copy of the
target JAR or directory will be made.
Enhance Classes
This modifies the bytecode for all persistence –capable and –aware classes in
conformance with the JDO specification. You can use any conformant bytecode enhancer
with the MVCSoft runtime, and should be able to use your MVCSoft enhanced classes
with any other JDO product.
If the project option is “Enhance to Directory,” rather than “Enhance in Place,” a copy of
the target JARs or directories will be made into which the modified classes are inserted.
Database
The Database top-level menu has three menu items: Create Tables…, Remove Tables…,
and Display Tables....
Create Tables…
This menu item displays the “Create Tables” dialog box, which is used to create the
required tables for your persistent classes. Before the database tables can be created, the

98
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

metadata class needs to have been compiled. The DDL (and any error messages) are
displayed in the dialog box’s message pane. The Create Tables dialog box has seven
options:
1) Driver—this is the name of the JDBC driver for the target DBMS. For instance,
the driver name for an SapDB database might be
com.sap.dbtech.jdbc.DriverSapDB.
2) Driver JAR—this is the JAR for the JDBC driver. You can alternatively place any
JARs for the JDBC driver on the MVCSoft GUI classpath and leave this blank.
3) DB URL—this is the URL for the target database. For instance, the driver name
for an SapDB database might be jdbc:sapdb://localhost/TST.
4) User—this is the user name for the database.
5) Password—this is the password for the database.
6) MetaData Class Name—this is the metadata class name. It will be filled in
automatically from the project information. If you have an earlier version of the
class with a different name, you can access the class by changing the name here.
7) Additional Factory Properties—these are additional properties that can be passed
to the PersistenceManagerFactory that the GUI instantiates when it issues DDL.
See the chapter on PersistenceManagerFactory properties for more information.
Remove Tables…
This menu item displays the “Remove Tables” dialog box, which is used to remove the
tables used for your persistent classes. Before the database tables can be removed, the
metadata class needs to have been compiled. The DDL (and any error messages) are
displayed in the dialog box’s message pane. The Remove Tables dialog box has the same
options as the Create Tables dialog box.
Display Tables...
This menu item can be used to show the DDL for the schema without interacting with a
database. The only two options are the MetaData Class Name and the Additional Factory
Properties, described above.
Options
The Options top-level menu has two menu items: Configure Project… and IDE
Settings….
Configure Project…
This menu item displays the “Configure Project Settings” dialog box. There are five tabs
of options for your project: the Basic Settings tab, the Class Sources tab, the Build Config
tab, Installed Data Transformers, and Installed Constant Transformers. On the Basic
Settings tab:

99
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ Generated Package Name—the metadata class package name. You will need to
supply the metadata class name to the JDO factory as a property.
♦ Generated Class Name—the metadata class simple name. You will need to supply the
metadata class name to the JDO factory as a property. (The fully qualified class name
is package name plus this class name.)
♦ Target for .class Metadata—you must select one of your class sources as a target for
the generated metadata before selecting the “Generate and Compile Descriptor”
option from the “Deploy” top-level menu.
♦ Optional Directory for .jdo Files—this is an optional setting used to direct all .jdo
metadata files to a specific class source, rather than the class source corresponding to
the .class bytecode. The most common use is to save the .jdo metadata with the .java
files.
♦ Database Target—database specific behavior (such as default SQL types) is
configured in an XML file. You can select from one of the XML files in the
configuration directory (which defaults to the “config” subdirectory of the
installation, but can be changed using the IDE Settings dialog). The format of the
XML file is described later in this documentation.
♦ Enhance in Place—you can enhance classes and generate metadata directly to your
source bytecode JARs and directories, or you can have the GUI copy them and
enhance and generate to the copies.
♦ Enhanced Bytecode Output Directory—if the “Enhance in Place” option is “Enhance
to Directory,” you specify the directory here.
On the Class Sources tab, there is a table that lists all the class sources (directories or
JARs) in your project. You can remove a class source by selecting the row and choosing
the Delete Selected button. You can add a directory class source by choosing the New
Directory button, and selecting a directory. You can add a JAR class source by choosing
the New JAR button, and selecting a JAR. There are four columns in the table:
♦ Type—a read only column that indicates whether the class source represents a
directory or JAR.
♦ File—a read-only column that indicates the file-system entity to which the class
source points. (This is selected when you choose the “New Directory” or “New JAR”
button.)
♦ Description—the name for the class source. You must specify a name if you want to
refer to this class source in any other configuration option. Otherwise, it is optional.
♦ Library—a checkbox that indicates whether the JAR or directory is just listed as a
library. The only effect of this checkbox is to keep the class source out of the tree
pane listing.
On the Build Config tab:

100
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Project Base Relative to Settings File—if a file name used for any purpose (class source,
persistence capable or aware list file name, etc.) can be described relative to the project
base directory, that is how the information is stored in the project XML file. One
advantage of this is to facilitate use with version control systems (allowing checkout to
different computers and file structures). The project base directory is ordinarily
determined by taking the directory of the project settings XML file. You can change that
here, typically by specifying a relative path something like “/..”. An example of the
common use for this feature would be if the settings file were stored in a “config”
directory of your project, rather than in the base directory.
♦ Default Names Class—if a name for a table or field is not specified in the
configuration information for the class, relationship, or field, a default name is
generated. You can change the algorithm for the default name generation by
specifying a class name here, and making it available to the GUI. The class must
implement the following interface:
package com.mvcsoft.jdo.user;

import com.mvcsoft.jdo.runtime.util.FieldFacade;
import com.mvcsoft.jdo.runtime.util.ClassFacade;

import java.util.Collection;

public interface DefaultNames


{
public static final int PARENT = 0;
public static final int KEY = 1;
public static final int VALUE = 2;

public String tableName( ClassFacade cl );


public String tableName( ClassFacade parentClass, String
relationshipField );
public String columnName( ClassFacade cl, FieldFacade f );
public String datastoreKeyColumnName( ClassFacade cl );
public String indexColumnName( ClassFacade cl );
public String lockColumnName( ClassFacade cl, String lockType );
public String relationshipColumnName( ClassFacade cl, FieldFacade f,
int whichRelationship );
public String relationshipColumnNameForDatastoreKey( ClassFacade cl,
FieldFacade f, int whichRelationship );
public String discriminatorColumnName( ClassFacade cl );
public String discriminator( ClassFacade cl, Collection
allClassesInHierarchy );
public String fkConstraint( ClassFacade parentClass, String
relationshipField, int whichRelationship );
public String fieldConstraint( ClassFacade parentClass, FieldFacade
f, String constraintType );
public String indexConstraintName( ClassFacade cl );
}
Class facades and field facades provide access to the class information using the same
methods as the corresponding standard Java reflection classes. For an example of the

101
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

implementation of this class, see


com.mvcsoft.jdo.generator.defaults.StandardDefaultNames. (This class is used if no
default names class is specified.)
It’s possible to use this interface creatively, e.g. to maintain a repository of SQL mapping
information and access the names from this at metadata generation time.
♦ Default Types Class—this class is similar to the default names class, although will be
used less frequently. The more usual path to set defaults is to specify a database-
specific XML configuration file. You might provide an implementation of this class
to support repository-driven development. To provide your own, specify a class that
implements the following interface:
package com.mvcsoft.jdo.user;

import com.mvcsoft.jdo.runtime.util.FieldFacade;
import com.mvcsoft.jdo.runtime.util.ClassFacade;

public interface DefaultTypes


{
public int jdbcType( ClassFacade cl, FieldFacade f );
public String sqlType( ClassFacade cl, FieldFacade f );
public int jdbcMethod( ClassFacade cl, FieldFacade f );
public String transformer( ClassFacade cl, FieldFacade f );
public String constantTransformer( ClassFacade cl, FieldFacade f );
public String mappingTransformer( ClassFacade cl, FieldFacade f);
public String discriminatorColType( ClassFacade cl );
public java.util.Map getWildcardPatternsToSubstitutions();
}

♦ Custom Config File—this XML file, in the same format as the Database Target
config file, allows you to override specific instances in a Database Target file without
rewriting the entire file
♦ Persistence Capable List File—allows you to specify the file name for the persistence
capable class list, rather than accept the default
♦ Persistence Aware List File—allows you to specify the file name for the persistence
aware class list, rather than accept the default
♦ Generated Source Output Directory—allows you to specify the directory to which the
metadata descriptor .java file is generated. This is useful for debugging purposes. If
no directory is specified, a temp directory is used.
♦ Save XML Metadata At Level—allows you to specify whether the .jdo metadata is
stored at the class, package, or project level. The default is package level.
♦ Project-level Metadata File Name—if the metadata is saved at the project level, the
file name is specified here.

102
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

On the Installed Data Transformers tab, you can list the user-defined classes that
provide transformations of data types between the object state and the database. Listing
them in the project allows them to be selected from various comboboxes in the GUI.
On the Installed Constant Transformers tab, you can list the user-defined classes that
provide transformations of data types between the JDO-QL constants for field values and
the database. Listing them in the project allows them to be selected from various
comboboxes in the GUI.
IDE Settings…
This menu item displays the “Configure Settings” dialog box to provide settings for the
GUI IDE. These settings are stored in a properties file, and are not specific to any project.
Most of the settings actually provide defaults for projects. The options are:
♦ Default Names Class—this provides the default “Default Names Class” if none is
specified in the project.
♦ Default Types Class—this provides the default “Default Names Class” if none is
specified in the project.
♦ Config Directory—this provides the directory from which XML files are read to
provide selections for the “Database Target” configuration in the project.
♦ Database Target—this provides the default Database Target setting if none is
specified in the project.
♦ Source Output Directory—this provides the default Source Output Directory setting if
none is specified in the project.
♦ Temporary Directory for Compiles—the Java compiler outputs the metadata .class
file to a directory, before it is added to the appropriate “class source” directory or
JAR. This directory is used if specified; otherwise, a temp directory is used.
♦ Enhanced Bytecode Output Directory—this provides the default output directory
setting if none is specified in the project.
♦ Save XML Metadata at Level—this provides the default level at which the XML
metadata is saved if none is specified in the project.
♦ Enhance in Place—this provides the default Enhance in Place behavior if none is
specified in the project.
Help
The Help top-level menu has three standard help menu items, that display the About
dialog box, context help, and the help index.
Fault Group Configuration Screen
Fault groups are added to base classes in an inheritance hierarchy (which includes classes
with no persistence capable superclasses or subclasses). To get the list of base classes,

103
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

choose the “Fault Groups” filter from the tree panel. To see the fault group configuration
screen, click on the appropriate base class.
You can add a fault group by clicking the “Add Group” button and giving a unique (in
that class) name in the dialog box. You can select an existing group for configuration by
selecting it from the “Edit This Fault Group” combo box. You can delete the selected
group by clicking the “Delete Current” button.
There are two tree controls in the work area of the fault group configuration screen. The
left tree control shows the available fields and relationships. The right tree control shows
the current fields and relationships in the fault group. (A relationship with no fields will
load only the key value or values.)
You can move fields and relationships back and forth by selecting them and clicking the
“Add >” or “< Remove” buttons.
Key Factory Configuration Screen
To get a list of key factories, choose the “Key Generators” filter from the tree panel. To
add a key factory, select “Add Key Factory…” from the Edit top-level menu. To
configure a key factory, select it in the tree panel. You will get one of two screens,
depending on whether the key factory is based on universally unique identifiers (UUID)
or blocks retrieved from a database table (blocks).
Configuring a UUID Factory
A UUID factory generates a key value based on a combination of factors that will
provide statistical uniqueness. You can indicate the desired number of bytes from five
sources: a timestamp, the IP address of the host computer, the hash value of the UUID
class, the start time of the class, and a counter.
♦ Timestamp Bytes—This is the millisecond time value at the instant the key is created.
A timestamp represents the number of milliseconds since midnight, January 1, 1970
UTC, and has 8 bytes. This is the primary way that unique values are generated on a
particular machine, and you should use enough bytes to prevent cycling. MVCSoft
recommends six bytes. (Four bytes will recycle every 50 days!)
♦ IP Address Bytes—This is the IP address of the machine on which the JDO runtime is
executing. This creates unique values within a network, in case two JDO runtimes
create instances at the same millisecond. There are four bytes in the value, and
MVCSoft recommends that you should use all 4 bytes.
♦ System Hash Bytes—This is the hashcode of the sequence generating instance. It is
unique to a key factory within a JVM, and helps to differentiate values between JVMs
on the same machine that might create key values at the exact same millisecond.
MVCSoft recommends that you should use at least 2 bytes.
♦ Class Start Time Bytes—This is the startup timestamp of the sequence generating
instance. It is unique to a key factory, and serves to differentiate values between

104
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

JVMs on the same machine that might create key values at the exact same
millisecond. MVCSoft recommends that you should use at least 2 bytes.
♦ Counter Bytes—This is a revolving counter. It serves to distinguish between key
values created in the same JVM at the same millisecond. MVCSoft recommends that
you should use at least 2 bytes.
♦ You can also provide a value for the default SQL type of the datastore key.
Configuring a Blocks Factory
A blocks factory (also known as a sequence factory) retrieves a key value from a block of
values reserved in a database table. The connection used by this key factory must be non-
transactional, to ensure that the application will perform well and behave correctly in
high-concurrency situations. This means that if connection factories are being used (e.g.
in a managed environment such as a servlet or EJB container), the
ConnectionFactory2Name property—the non-transactional connection factory—must be
provided to the PersistenceManagerFactory configuration.
The database table that maintains sequence information has two columns. The sequence
name is the primary key column. A sequence name is just an arbitrary string, with which
you can use the same table to maintain information about independently incrementing
sequences. The other column maintains the highest sequence number that has been
reserved.
The MVCSoft JDO Toolkit gives you great flexibility in saving your sequence
information. Multiple key factories may use one seqence in a single table, a sequence per
factory, or even a separate table per factory. (The performance implications are negligible
for any of these strategies, as long as the block size reasonably large.)
There are eight properties that you must configure to use this strategy for producing
datastore keys: Chunk Size, Retry Count, Table Name, Sequence Name, Sequence Col
Name, Sequence Col Type, Increment Col Name, and Increment Col Type.
♦ Chunk Size—This is the number of ids that are reserved in a single database access.
This reservation of "chunks" of sequences is an important reason that this technique
scales effectively. One strategy is to make the underlying key of type long, so that
there is little reason not to use a very, very large chunk size. A good choice might be
10,000, ensuring that the database will rarely be accessed.
♦ Retry Count—The runtime updates the sequence table using optimistic concurrency.
This value determines how many times it will try to retrieve a new "chunk" before
throwing an exception to the application.
♦ Table Name—The database table name for the sequences.
♦ Sequence Name—This is an arbitrary constant string with which to associate the
sequence information in the database.
♦ Sequence Col Name—The column for the sequence name.

105
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ Sequence Col Type—The SQL type for the sequence name column (a string type, e.g.
varchar(15)).
♦ Increment Col Name—The column name for the value of the next available block.
♦ Increment Col Type—The SQL type for the increment column (a numeric type). This
is also used as a default type for the datastore key column if one is not specified in the
entity.
Class Settings Configuration Screens
You can access the class settings by selecting the persistent capable class in the tree pane.
(Use any of the following three filters: Persistence Capable; Capable and Aware; or All
Objects.) There are three panels for the class settings in the work pane: Basic Info;
Datastore Key Config; and Locking Config.
Basic Info
The Basic Info tab has seven properties: Name, Table, Key Class, Requires Extent,
Discriminator, Disc Col Name, and Disc SQL Type.
♦ Name—This read-only field displays the fully-qualified name of the persistence-
capable class for reference purposes.
♦ Table—This is the name of the table where the simple persistent state of the object
will be stored. (Relationship information is configured separately.) A reasonable
default will be provided from the project DefaultNames instance.
♦ Key Class—For application identity only, this is the fully-qualified name of the key
class. There is no default.
♦ Requires Extent—This property is defined by the JDO spec, and specifies whether an
extent must be managed for this class. The PersistenceManager getExtent method can
be executed only for classes where this property is true (the default).
♦ Discriminator—This property is required only for classes that participate in an
inheritance hierarchy with two or more persistence-capable classes. The type of class
is stored as a string in the base class table, so that when the class is read from the
database the correct subtype can be instantiated. The default discriminator is the
fully-qualified name of the concrete subclass. However, you can specify any string
here. This is useful both to improve performance (e.g. the discriminator “C” is more
efficient than the discriminator “com.somecompany.busobjs.Customer”), and to
conform to any pre-existing type identification scheme in an existing database
schema.
♦ Disc Col Name—This is the column name for the base table discriminator. You do
not need to specify it for derived classes. A reasonable default will be provided from
the project DefaultNames instance.

106
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ Disc SQL Type—This is the column SQL type for the base table discriminator. You
do not need to specify it for derived classes. A reasonable default will be provided
from the project DefaultTypes instance.
Datastore Key Config
This tab is used for classes with datastore identity only. You do not need to use it for
classes with application identity. You select one of the key generators you have
configured, and then configure the datastore key column values. Reasonable defaults will
be provided for everything; this configuration screen is not mandatory. (There is a default
key factory that uses the UUID algorithm with MVCSoft recommended values and a
java.lang.String type.) You can also set just the key factory and let it provide defaults for
the other values. Finally, you can configure all of the following values.
♦ Key Generator Name—This is the name of one of the key generators you have
configured. The key generator will determine the algorithm for producing new key
values.
♦ Java Type—This is the class of the key that will be associated with your persistence
capable class. The choices are STRING, BIGINT, LONG, and INT. A default will be
provided from the key factory. However, it may not be consistent with other values,
and so if any of Java Type, Jdbc Type, PreparedStatement Method, and SQL Type are
specified, they should all be specified.
♦ Column Name—This is the database column name of your datastore key. A
reasonable default will be provided from the project’s DefaultNames instance if this
value is not specified.
♦ Jdbc Type—This is the JDBC type for the runtime to use in accessing your datastore
key. A default will be provided from the key factory. However, it may not be
consistent with other values, and so if any of Java Type, Jdbc Type,
PreparedStatement Method, and SQL Type are specified, they should all be specified.
♦ PreparedStatement Method—This is the stem name of the JDBC method that is used
to set information in the prepared statement. A default will be provided from the key
factory. However, it may not be consistent with other values, and so if any of Java
Type, Jdbc Type, PreparedStatement Method, ResultSet Method, and SQL Type are
specified, they should all be specified.
♦ ResultSet Method—This is the stem name of the JDBC method that is used to retrieve
information from the result set. A default will be provided from the key factory.
However, it may not be consistent with other values, and so if any of Java Type, Jdbc
Type, PreparedStatement Method, ResultSet Method, and SQL Type are specified,
they should all be specified.
♦ SQL Type—This is the SQL type for the datastore key column. A default will be
provided from the key factory. However, it may not be consistent with other values,
and so if any of Java Type, Jdbc Type, PreparedStatement Method, and SQL Type are
specified, they should all be specified.

107
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ Data Transformer—This is a runtime class that will perform operations on the data
before it is saved to the database, and after it is read from the database. The most
likely class to be needed here is “BigInteger as BigDecimal,” because JDBC methods
don’t operate on BigInteger classes directly.
Locking Config
The MVCSoft JDO Toolkit provides you with three framework-level locking strategies.
These locking strategies provide you with the runtime support to scale your applications
while maintaining database consistency.
A transactional database provides an application with the ability to store data with
A.C.I.D. properties: atomic, consistent, isolated, and durable. However, perfect
application of these properties will require each update of the data to be serialized, and
for any write of the data to block reads. In other words, concurrency would be prevented,
and your application could not scale with increased demand.
In practice, concurrent access to the data is usually allowed, and the A.C.I.D. properties
of a transaction are not perfectly applied. However, this requires application support, in
case the data is changed within a transaction--between the time the data is read, and the
data is written. The MVCSoft optimistic locking strategies provide this "application
support," which allows your enterprise-class application to scale without making the
database a bottleneck.
The three strategies available are: Counter; Timestamp; and Compare Fields. These are
all optimistic locking strategies. Although optimistic locking can help your application
achieve a high degree of concurrency, the trade-off is that your application must be
prepared to deal with an exception (the JDOCanRetryException) in the event of two
conflicting updates.
Here is an example of a sequence of events that will lead to an optimistic locking
exception (and transaction rollback). Any locking conflict will necessarily involve
multiple PersistenceManagers, although they can be in the same or different JVMs:
1) Object A associated with PersistenceManager 1 reads data during a transaction.
2) Object B with the same identity associated with PersistenceManager 2 reads the
same data during a transaction.
3) Object A successfully updates the data.
4) Object B attempts to update the data, but finds it has changed since the read
occurred. Transaction 2 is rolled back, and an exception is thrown.
Compare Fields Strategy
The concept behind field-based optimistic locking is simple. To determine if there has
been a concurrent modification during a transaction, the Persistence Manager simply
compares the state values as they were read to the state values in the database. A simple
example will make this clear (please note that certain simplifying assumptions have been
made, such as ignoring the use of prepared statements):

108
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

1) A persistent object representing a customer exists in the database with the


following values: id=1; name=Dan; city=Marblehead; state=MA; zip=01945
2) The JDO runtime loads the customer object's state with the following SQL: select
name, city, state, zip from customer where id=1
3) A client modifies that state, e.g. setCity( "Schenectady" ); setState( "NY");
setZip( "12306" );
4) The Persistence Manager attempts to save the customer bean's state with the
following SQL: update customer set city='Schenectady', state='NY', zip='12306'
where id=1 and city='Marblehead' and state='MA' and zip='01945'
In this example's where clause, the "id=1" statement is sufficient to identify the correct
bean instance. The remainder of the where clause is a test for concurrent modifications. If
any concurrent modifications have taken place, no instances will be found to update. If
the update statement indicates that no rows have been updated, the Persistence Manager
throws an exception.
This locking strategy only checks concurrent modifications against fields that you read
within the transaction. In other words, if there is a concurrent modification against a field
you did not use within the transaction, there will be no rollback. This is almost always the
desired behavior. (Note that the other two optimistic locking strategies check against any
modification to the record.) If you do not read any data before making your update, the
result is "don't care" semantics: there is no chance for an optimistic rollback. Again, since
the update did not consider the current state of the database, this is probably the desired
behavior.
Certain fields should not or can not be used in an optimistic locking comparison, e.g.
large binary objects. You can set a particular field for exclusion, on the field’s Basic Info
screen. Field-based optimistic locking does not require any changes to your database
schema, nor does it require the support of non-JDO applications.
Counter and Timestamp Strategies
The other two optimistic locking strategies--counter and timestamp--are quite similar.
Rather than compare the values of all the fields that have been read, they compare the
value of a single field whose sole purpose is to protect against concurrent modifications.
When a read is made, the value of this field is also read.
When an update is made, the value of this field checked in the where clause against the
original value. A changed value indicates a concurrent modification, and an exception is
thrown.
In the case of the counter strategy, the numerical value is incremented during this update.
In the case of a timestamp value, the time of the modification is recorded.
You should note that both these strategies require the addition of a column to the
database table. You can configure the properties of the column like you can any other,
with the column name, jdbc type, prepared statement type, sql type, and data transformer.

109
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Fields for these values will appear when either the counter or timestamp strategy is
chosen.
You should also note that both these strategies require support from legacy applications.
In other words, if a legacy application updates the table, it must also update the counter or
timestamp field.

The timestamp strategy is provided for compatibility with legacy code that
uses this strategy. It is not ideal for two reasons: the potential, however
unlikely, for multiple updates to have the same timestamp; and the
potential for rounding errors in storage formats that can prevent this
strategy from working with some databases. The counter strategy probably
should be used in preference between these two.

Field Settings Configuration Screens


You can access the field settings by selecting the persistent capable class in the tree pane
and navigating to the field you want to configure. (Use any of the following three filters:
Persistence Capable; Capable and Aware; or All Objects.) There are eight tabbed pages
that can be used to configure field values: Basic Info, DB Column Configuration,
Mapping Transformation, Relationship Mapping, java.util.Map Mapping, Indexed
Relationship, Relationship Key (Basic Type), and Relationship Value (Basic Type).
However, only a subset of them will be relevant to a particular field. There are four basic
scenarios: configuring a simple property; configuring a single-valued relationship;
configuring a collection-valued relationship; and configuring a map-based relationship.
Configuring a Simple Property
There are three tab pages potentially relevant to a simple property (e.g. fields of primitive
types, string fields, wrapper types, serialized objects). They are Basic Info, DB Column
Configuration, and Mapping Transformation. For many development scenarios, you
might only specify the column name on the DB Column Configuration page (or not even
this, if you are happy with the generated default name).
Configuring a Single-Valued Relationship
There are two tab pages potentially relevant to a single valued relationship (i.e. a field of
a persistent object type). They are Basic Info and Relationship Mapping. You will always
want to specify “Map As” and “Value Type” on the Basic Info tab page. You will usually
want to configure the database mapping and the fault group on the Relationship Mapping
page (unless you are happy accepting the defaults).
Configuring a Collection-Valued Relationship
There are four tab pages potentially relevant to a collection valued relationship (i.e. a
field of type java.util.Collection or its subclasses, or an array mapped to a relationship).
They are Basic Info, Relationship Mapping, Indexed Relationship, and Relationship
Value (Basic Type). You will always want to specify “Map As” and “Value Type” on the

110
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Basic Info tab page. You will usually want to configure the database mapping and the
fault group on the Relationship Mapping page (unless you are happy accepting the
defaults). If you want to control whether the relationship ordering is maintained with an
index field (and the database column in which the index is stored), you can configure this
on the Indexed Relationship tab. If the contents of the collection are of a string or
primitive wrapper type, you can configure the column on the Relationship Value (Basic
Type) screen.
Configuring a Map-Based Relationship
There are four tab pages potentially relevant to a map valued relationship (i.e. a field of
type java.util.Map or its subclasses). They are Basic Info, java.util.Map Mapping,
Relationship Key (Basic Type), and Relationship Value (Basic Type). You will always
want to specify “Map As,” “Key Type,” and “Value Type” on the Basic Info tab page.
You will usually want to configure the database mapping and the fault groups on the
java.util.Map Mapping tab page. If the key type of the map is a string or primitive
wrapper, you can configure the column on the Relationship Key (Basic Type) screen. If
the value type of the map is a string or primitive wrapper, you can configure the column
on the Relationship Value (Basic Type) screen.
Basic Info Tab
There are twelve properties on the Basic Info tab: Name, Class, Persistence Type, Key
Field, Check for Reachability, Map As, Key Type (for maps only), Value Type, Inverse
Relationship, Second Class Object, Exclude From Lock Comparison, and Ordering
Object.
Name
This is the name of the field in the Java class. It is read only and included for reference.
Class
This is the declared type of the field in the class. It is read only and included for
reference.
Persistence Type
The JDO specification defines three persistence types for a field: persistent, transactional,
and non-persistent non-transactional. A persistent field is one for which the state is
maintained in the database. A transactional field is one for which the runtime preserves
and restores the state during certain state transitions. A non-persistent non-transactional
field is just an ordinary field managed entirely by the application and not the JDO
runtime.
If you do not select a type for the field, a default type is chosen based entirely on rules
provided by the JDO specification. By default, a field is either persistent or non-persistent
non-transactional. A field will never be simply transactional (and non-persistent) by
default.

111
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

If a field is declared transient, it will be non-persistent non-transactional by default. (You


can make a transient field be persistent for JDO. It just won’t be so by default.)
Otherwise, the following field types are persistent by default (see JDO 1.0 specification
section 18.4):
♦ fields of an object type that is also persistence-capable (i.e. single-valued
relationships)
♦ primitives: boolean, byte, short, int, long, char, float, double
♦ java.lang wrappers: Boolean, Byte, Short, Integer, Long, Character, Float, Double
♦ java.lang: String, Number
♦ java.math: BigDecimal, BigInteger
♦ java.util: Date, Locale, ArrayList, HashMap, HashSet, Hashtable,LinkedList,
TreeMap, TreeSet, Vector, Collection, Set, List, and Map
♦ arrays of primitive types, java.util.Date, java.util.Locale, java.lang and java.math
types specified above, and persistence-capable types
For the most part, these encompass every field type you might typically use in creating
the Java definition of a persistent class. If you want these field types to be persistent, you
can just accept the default. If you want them to be transactional only, or non-persistent
non-transactional, you must change the setting from the default. If you have a field that
you want to make persistent that does not appear in this list, e.g. an object that you want
serialized, you must explicitly declare it to be persistent.
Key Field
For application identity only, you must identify the key fields here. (It is not necessary to
set non-key fields to false; that is the default.)
Check for Reachability
Any object reachable by field navigation starting from a persistent object is itself made
persistent. This applies to both relationship fields and simple state fields. (For instance,
you might have a collection or map that you intend to save as a serialized field. The
contents of this field will still be checked for objects to be made persistent by
reachability.)
If you know that a particular object, collection, or map valued field will not require this
check, you can avoid it by selecting false. (The default is true.) This will save some
runtime operations, which may be significant depending on the number of objects that the
runtime would have to inspect.
Map As
For fields with a type of collection, map, or persistence-capable object, you can elect to
have them mapped as a column (i.e. serialized) or mapped as a relationship in the
database. You should only consider serialization in special circumstances: the object to

112
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

be serialized will not need to be accessed by non-Java programs; its serialized format will
not change in an incompatible way; and a relationship mapping is not appropriate or
possible.
You must indicate the mapping strategy to have access to the other appropriate
configuration screens.
Key Type (for maps only)
This setting is only required for java.util.Map fields that have been mapped to a
relationship. The relational database mapping depends on a consistent type of key—either
a basic type or a persistence capable type. You can make full use of inheritance. In other
words, a map key with a declared base type of com.mycompany.MyBaseClass can have
actual instances of MyDerivedClass1, MyDerivedClass2, and so forth.
Value Type
This setting is required for all fields mapped to a relationship. For single- and collection-
valued relationships, this is the persistent type of the field or the basic or persistent type
contained in the collection. For a java.util.Map field, this is the value type. As with the
Key Type, you can make full use of inheritance.
Inverse Relationship
The JDO 1.0 specification does not contain any provision for managed or reciprocal
relationships. MVCSoft provides an extension that allows you to map two relationships
to a single relational database construct. One the Value Type is set, you can pick an
appropriate inverse relationship field here. Both relationships will now use the same
configuration data. You should treat each side of the relationship independently in your
code, continuing to update and load both sides. This is a feature to enable a particular
mapping in the database, and not a proprietary version of managed relationships.
Second Class Object
The JDO specification allows the runtime to replace mutable types with “second class
persistent objects.” These will be objects that extend the basic mutable Java type. The
MVCSoft JDO Toolkit will always provide a default SCO type, but sometimes there will
be multiple options. You can specify the second class object that you would prefer.
Exclude From Lock Comparison
This information is only relevant to classes that use the “compare fields” optimistic
locking strategy. You can exclude a field from being included in the lock clause in the
update SQL statement.
Ordering Object
This information is only relevant if the contents of the field will be a java.util.TreeSet or
java.util.TreeMap. You can enter the fully-qualified name of a class that implements the

113
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

java.util.Comparator interface. This ordering object will then be passed to the second-
class object extending java.util.TreeSet or java.util.TreeMap
DB Column Configuration
This tab page deals specifically with how a field is mapped to a database column, if and
only if there is a one-one mapping between field and column. (The Mapping
Transformation screen is used if there is a one-many relationship between field and
column.) There are ten properties on the DB Column Configuration Screen: Column
Name, JDBC Type, PreparedStatement Method, ResultSet Method, SQL Type, Data
Transformer, Constant Transformer, Fault Group, Index Name, and Index Type.
Column Name
This is the name for the database column that represents the field. A default will be
created based on the algorithm in the project’s DefaultNames instance.
JDBC Type
This is the JDBC type used to move the data between the column and the database. It is
used if the PreparedStatement method is setObject, and also passed as a parameter to user
or framework-defined value transformers, where it may or may not be used. (The
framework uses it only in the UtilDateTransformer, which transforms a java.util.Date
instance into one of java.sql.Date, java.sql.Time, or java.sql.Timestamp based on its
value.)
A default will be provided if this value is not specified, based on the algorithm in the
project’s DefaultTypes instance. Most likely, the DefaultTypes instance will be the
MVCSoft-supplied one that consults the appropriate database-specific configuration file
for the default based on the Java type of the field.
You will usually want to set this value if you have a field of type java.util.Date, to
indicate the precision with which the date should be stored in the database.
PreparedStatement Method
This is the actual method used in the JDBC PreparedStatement to set data.
A default will be provided if this value is not specified, based on the algorithm in the
project’s DefaultTypes instance. Most likely, the DefaultTypes instance will be the
MVCSoft-supplied one that consults the appropriate database-specific configuration file
for the default based on the Java type of the field.
You might want to override this method if you have installed a data transformer class that
changes the data type. (For instance, a transformer might change a java.util.Date in the
Java class into a java.lang.String before it is stored in the database. The date getter and
setter functions would no longer be appropriate.)
ResultSet Method
This is the actual method used in the JDBC ResultSet to retrieve data.

114
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

A default will be provided if this value is not specified, based on the algorithm in the
project’s DefaultTypes instance. Most likely, the DefaultTypes instance will be the
MVCSoft-supplied one that consults the appropriate database-specific configuration file
for the default based on the Java type of the field.
You might want to override this method if you have installed a data transformer class that
changes the data type. (For instance, a transformer might change a java.util.Date in the
Java class into a java.lang.String before it is stored in the database. The date getter and
setter functions would no longer be appropriate.)
SQL Type
This is the type for the column in the database representing the field. It is used only for
DDL when you use an MVCSoft tool to create the schema for your objects. (If you use a
preexisting schema, there is no reason to specify this value other than as documentation.)
A default will be provided if this value is not specified, based on the algorithm in the
project’s DefaultTypes instance. Most likely, the DefaultTypes instance will be the
MVCSoft-supplied one that consults the appropriate database-specific configuration file
for the default based on the Java type of the field.
You will often want to override this value to be more appropriate to the characteristics of
the data in your object. (For example, there is probably no single char or varchar length
appropriate for all the strings in your application.)
Data Transformer
The MVCSoft JDO Toolkit provides for maximum flexibility by allowing you to specify
arbitrary data transformations between the object state and the database. Data
transformers must simply implement the following interface (in a threadsafe manner),
and be available at runtime:
package com.mvcsoft.jdo.runtime.user;

public interface ValueTransformer


{
public Object fromDatabaseToState(Object databaseValue);
public Object fromStateToDatabase(Object stateValue, int jdbcType);
}
The simplest, safest, and most performant way to make this class threadsafe is to not have
any state variables. This approach is strongly recommended.
There are eleven predefined data transformers that ship with the MVCSoft product. They
are:
♦ BigInteger as BigDecimal—The JDBC methods do not support BigInteger, but do
support BigDecimal. This transformer simply takes the BigInteger instance and saves
it as/restores it from a BigDecimal instance.
♦ Character as Number—The JDBC methods do not support instances of char directly.
This transformer takes the numerical value of the character and stores it as a number.

115
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ Character as String—The JDBC methods do not support instances of char directly.


This transformer converts the character to a String instance.
♦ char[] as String—This transformer treats a char[] as a String, rather than allowing it to
be serialized.
♦ Serialization with Object->Key Replacement—This transformer is used to serialize
objects—with a twist. Any persistent capable object is replaced with its key during
serialization prior to saving to the database, and restored from its key when the object
is restored from the database.
♦ Simple Serialization—This transformer serializes objects prior to storage, and
restores them from their serialized form on retrieval.
♦ java.util.Date as Long—This transformer allows a java.util.Date to be stored as a long
numerical value. (This might be used to circumvent inconsistencies between various
databases’s date storage format.)
♦ java.util.Date as java.sql.xxx—This commonly used transformer transforms a
java.util.Date into the appropriate java.sql instance based on the JDBC type.
♦ Boolean to y/n—This transformer stores a boolean value in the object state as a ‘y’ or
‘n’ in the database. It should usually be used with the “Boolean as y/n” constant
transformer, so that JDO queries are translated into SQL correctly.
♦ Boolean to t/r—This transformer stores a boolean value in the object state as a ‘t’ or
‘f’ in the database. It should usually be used with the “Boolean as t/f” constant
transformer, so that JDO queries are translated into SQL correctly.
♦ Boolean to 1/0—This transformer stores a boolean value in the object state as a 1 or 0
in the database. It should usually be used with the “Boolean as 1/0” constant
transformer, so that JDO queries are translated into SQL correctly.
♦ String as CLOB—This transformer helps move a String back and forth between a
CLOB field in the database. It transforms the String into a StringReader for the set
method on the JDBC PreparedStatement, and it reads the String from the CLOB
instance from the JDBC ResultSet.
♦ byte[] as BLOB—This transformer helps move a byte[] back and forth between a
BLOB field in the database. It transforms the byte[] into a stream for the set method
on the JDBC PreparedStatement, and it reads the byte[] from the BLOB instance from
the JDBC ResultSet.
♦ Simple Serializable as BLOB—This transformer bundles the common case where you
would like to use both the simple serializable transformer and the byte[] as BLOB
transformer to serialize a Java object into a BLOB field in your table. for the set
method on the JDBC PreparedStatement, and it reads the byte[] from the BLOB
instance from the JDBC ResultSet.

116
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Constant Transformer
The constant transformer is used to transform a JDO query constant to an appropriate
form for comparison to the database stored value (to correspond to the effects of the data
transformer). It must implement the following interface:
package com.mvcsoft.jdo.runtime.user;
public interface ConstantTransformer
{
public String transformConstant( int type, Object value );
}
Fault Group
The fault group is used to determine the set of data to be loaded along with this field,
when it is “faulted in” at runtime. This occurs when the field is accessed by the
application and is not current in the object’s state. If a fault group is not provided, the
runtime will determine a reasonable default. (See the chapter on fault groups for more
information.)
Index Name
This is the name of the column index, if specified. Assuming you use the default index
creation template, this will be used in the DDL.
Index Type
You can specify an index, a unique index, or no index (the default).
Mapping Transformation
If there is a one-many mapping between a field and its database columns, use this screen
instead of the DB Column Configuration screen (except for the Fault Group setting).
Choose the appropriate mapping transformer. Right now only the Locale Mapping
transformer for java.util.Locale instances is available, but two more are planned: one for
embedded persistent objects, and one for embedded Java Beans that aren’t enhanced to be
persistence capable.
For each row, fill in the information as described in the above documentation for the DB
Column Configuration tab.
Relationship Mapping
For single or collection valued relationships (but not map valued relationships), this
screen is used to configure the database mapping and specify the fault group. The first
two fields are:
♦ Map As—Most relationships can be mapped to use either a relationship table or a
foreign key in one of the endpoints. For one-one relationships, the foreign key can be
in either endpoint. For one-many relationships, the foreign key must be in the “many”
side. For many-many relationships, the mapping must be to a relationship. For one-
many relationships to a “basic type” (e.g. a collection of Strings), it doesn’t make

117
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

sense to have a relationship table and the mapping defaults to a foreign key
relationship. When a relationship mapping has been chosen, additional
configuration information will appear.
♦ Fault Group—If a relationship is lazily loaded at runtime (rather than eagerly loaded
during a find or query), the specified fault group will be used. If no fault group is
specified the runtime will provide a reasonable default. See the chapter on fault
groups for more information.
For a relationship table mapping, there are five additional configuration controls that
appear: Relationship Table, Parent Keys, Child Keys, Foreign Key Constraint Name (to
parent table), and Foreign Key Constraint Name (to child table).
♦ Relationship Table—This is simply the name of the table in the database that
maintains the relationship.
♦ Parent Keys—This is a table that allows you to provide the column names of the keys
in the parent class (i.e. the class that has the relationship field as a member).
♦ Child Keys—This is a table that allows you to provide the column names of the keys
in the target class (i.e. the class that you specified as the Value Type on the Basic Info
tab).
♦ Foreign Key Constraint Name (to parent table)—This allows you to specify the name
of the fk constraint between the parent object (i.e. the object with the collection class)
and the relationship table. If you don’t specify a name, a default will be provided by a
class implementing the DefaultNames interface. (Whether or not the foreign key
constraints are created is controlled by PersistenceManagerFactory configuration
parameters.)
♦ Foreign Key Constraint Name (to child table)—This allows you to specify the name
of the fk constraint between the child object (i.e. the object contained within the
collection class) and the relationship table. If you don’t specify a name, a default will
be provided by a class implementing the DefaultNames interface. (Whether or not the
foreign key constraints are created is controlled by PersistenceManagerFactory
configuration parameters.)
For a foreign key mapping, there are four additional configuration controls that appear:
Foreign Key Location, Value Object Table Name, and a table with key column names.
♦ Foreign Key Location—A relationship mapped to a foreign key must store the
relationship information in one of the classes that represent the endpoints. For one-
one relationships, the foreign key can be in either endpoint. For one-many
relationships, the foreign key must be in the “many” side. A “parent” is the class that
has the relationship field as a member. A “child” is the class that you specified as the
Value Type on the Basic Info tab.
♦ Value Object Table Name—If the relationship is a collection of basic types (such as
strings or numbers), these basic type values are stored directly in a database table

118
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

along with a foreign key value pointing to the parent. You can specify the table name
here. (If you do not, a default will be generated from the project’s DefaultNames
instance.)
♦ Table with key column names—If the foreign key is stored in the table representing
the parent endpoint, fields representing the child key will be placed in that table and
you can provide column names for those fields here. If the foreign key is stored in the
table representing the child endpoint, fields representing the parent key will be placed
in that table and you can provide column names for those fields here.
♦ Foreign Key Constraint Name—This allows you to specify the name of the fk
constraint. If you don’t specify a name, a default will be provided by a class
implementing the DefaultNames interface. (Whether or not the foreign key
constraints are created is controlled by PersistenceManagerFactory configuration
parameters.)
java.util.Map Mapping
A java.util.Map field mapped to a relationship will always have a relationship table in the
database schema. One column or set of colums will point to the parent object. One
column or set of columns will either store a “key” basic type value or point to a persistent
object record in another table that represents the key. One column or set of columns will
either store a “value” basic type value or point to a persistent object record in another
table that represents the value. This tab page has nine properties you can configure to
control the mapping and behavior of a java.util.Map field: Relationship Table, Key Fault
Group, Value Fault Group, FK Constraint Name (parent table), FK Constraint Name (key
table), FK Constraint Name (value table), a Parent table, a Key table, and a Value table.
♦ Relationship Table—This is the table in the database in which the map field’s
persistent state is stored.
♦ Key Fault Group—When a map-valued relationship is lazily loaded at runtime, the
specified fault group will be used for key objects. If no fault group is specified the
runtime will provide a reasonable default. See the chapter on fault groups for more
information.
♦ Value Fault Group—When a map-valued relationship is lazily loaded at runtime, the
specified fault group will be used for value objects. If no fault group is specified the
runtime will provide a reasonable default. See the chapter on fault groups for more
information.
♦ FK Constraint Name (to parent table)—This allows you to specify the name of the fk
constraint between the parent object (i.e. the object with the collection class) and the
relationship table. If you don’t specify a name, a default will be provided by a class
implementing the DefaultNames interface. (Whether or not the foreign key
constraints are created is controlled by PersistenceManagerFactory configuration
parameters.)

119
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ FK Constraint Name (key table)—This allows you to specify the name of the fk
constraint between the key object and the relationship table. If you don’t specify a
name, a default will be provided by a class implementing the DefaultNames interface.
(Whether or not the foreign key constraints are created is controlled by
PersistenceManagerFactory configuration parameters.)
♦ FK Constraint Name (value table)—This allows you to specify the name of the fk
constraint between the value object (i.e. the object with the collection class) and the
relationship table. If you don’t specify a name, a default will be provided by a class
implementing the DefaultNames interface. (Whether or not the foreign key
constraints are created is controlled by PersistenceManagerFactory configuration
parameters.)
♦ Parent table—This is a table that allows you to provide the column names of the keys
in the parent class (i.e. the class that has the map field as a member).
♦ Key table—This is a table that allows you to provide the column names of the keys in
the persistence capable key class. It is not used if the key class is a basic type.
♦ Value table—This is a table that allows you to provide the column names of the keys
in the persistence capable value class. It is not used if the key class is a basic type.
Indexed Relationship
For collection-valued or array-valued relationships, the order of items in the collection
may be significant. Relational database tables do not treat order as significant, so if the
application requires the order to be maintained, it must be stored as part of the
relationship state. Except for collections or arrays of basic types, the order is stored in the
relationship table. This means that if order is significant, the relationship must be
mapped to a table rather than to foreign keys. There are eight properties to be
configured on this screen: Indexed Relationship, Index Column Name, Index JDBC
Type, Index PreparedStatement Method, Index ResultSet Method, Index SQL Type, SQL
Constraint Name, and SQL Constraint Type. The Indexed Relationship property indicates
whether or not the order for the relationship should be maintained. The column name,
jdbc type, prepared statement method, result set method, sql type, constraint type, and
constraint name are used in the same way as for any persistent field. If you modify these
values you should make them appropriate for a Java class of type Integer. Note: the
default for a regular persistent field is to not have an index constraint. The default
for an index is to ***have*** an index constraint.
Relationship Key (Basic Type)
This page is only used for java.util.Map fields where the key is a basic type (such as a
java.lang.String, primitive type, or primitive wrapper). You can configure the same
properties as for any field of a persistence capable class: Column Name, Jdbc Type,
PreparedStatement Method, ResultSet Method, SQL Type, Data Transformer, or
Constant Transformer.
Relationship Value (Basic Type)

120
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This page is only used for collections, arrays, and values in java.util.Map fields where the
target is a basic type (such as a java.lang.String, primitive type, or primitive wrapper).
You can configure the same properties as for any field of a persistence capable class:
Column Name, Jdbc Type, PreparedStatement Method, ResultSet Method, SQL Type,
Data Transformer, or Constant Transformer.

121
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Fault Groups
A Java Data Objects implementation provides an object oriented view of persistent data.
Usually, this data will be stored in a relational database. It is not a simple problem to
translate between these two abstractions of persistent data—objects vs. tables. The
difficulties are often referred to as the object/relational impedance mismatch.
One of the biggest practical problems in translating from relational tables to objects is
known as the “n+1” problem. A data retrieval task that should (in relational terms) take
one or two queries, instead takes n+1 queries where n is the number of controlling
objects. In JDO, this is typically encountered with collection classes mapped to related
tables in the relational schema. Each object might lazily load its own collection class,
resulting in the original (1) query plus n additional queries where n is the number of JDO
objects. An example of this would be where a query returns customer objects, and then
each customer object lazily loads a collection of order objects. In a program that dealt
with relational constructs directly, the total number of queries would be two: one for the
customers, and one for the orders.

A version of the n+1 problem is found in Enterprise JavaBeans with bean


managed persistence. A finder query returns only the keys for the beans in
the result set. Subsequent calls to the ejbLoad life-cycle method create an
additional query for each key in the original result set. The total number of
queries is 1 (for the keys) + n (number of beans). This has profound
implications for the application’s ability to perform or to scale.

Of course, objects are often composed of multiple layers of other objects with multi-
valued relationships. Expanding on the above example, it is likely that the relationships
would be a customer has multiple orders, and an order has multiple line items (leaving
aside the likelihood that all these objects have additional relationships, such as customer-
>addresses or order->ship dates). The number of queries in a single unit of business logic
can quickly become unsupportable.
Consider a query that returns a hundred customers (hardly an unreasonable scenario!)
Assume that each customer has ten orders. To load customers, orders, and line items, you
would need one query for the customers, a hundred queries for the orders, and a thousand
queries for the line items. For one simple item of business logic, you would need 1,101
queries! And yet many object/relational mapping products ask you to accept precisely
this result.
Solution: Load Policies
The solution to this problem is to tell the runtime when the top-level query is issued that
you require additional data to be loaded in the retrieved objects. With this advanced
warning, the runtime can optimize the queries back down to the number you’d write if
you were using the relational paradigm. In the case of the example we’ve been using,

122
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

when you issued the query to retrieve the customer objects, you’d use a fault group to
specify a load policy that included the orders and the line items for those queries. Then,
rather than lazily loading each collection of orders and line items after the first use, the
runtime would eagerly load all that information: one query for the customer; one query
total for the orders; and one query total for the line items.

Why can’t the runtime just always eagerly load the related data? There are
two reasons. First, even in the simplest scenario, this creates unacceptable
database and runtime overhead if the data isn’t being used for a particular
transaction. Second, a common scenario would involve relationships that
could be navigated to include most or all of the entire database. There is
no way for the runtime to know where it should stop loading data, short of
the programmer telling it. That’s why, in the absence of other information,
the runtime defaults to lazily loading data.

Load policies can also be used to include single-valued relationship data in a query. They
can also be used to specify a subset of an object’s persistent state that should be loaded.
This is particularly useful if an object has state that is expensive to load, particularly large
binary or text fields.
Using MVCSoft Fault Groups
MVCSoft fault groups can be used in four places:
♦ Unloaded simple fields being refreshed
♦ Relationships being navigated
♦ JDO Query Language Queries
♦ PersistenceManager getObjectById retrievals
The first two scenarios are specified in the configuration data (i.e. by using the GUI), and
transparently used by the runtime when appropriate.
To use a particular fault group for a query or with getObjectById, you must cast the
javax.jdo.Query or javax.jdo.PersistenceManager interface to an MVCSoft interface,
com.mvcsoft.jdo.runtime.user. MVCSoftQuery or
com.mvcsoft.jdo.runtime.user.MVCSoftPersistenceManager, respectively. (The JDO
specification does not provide for a load policy mechanism beyond the simple “default
fetch group” mechanism, so an MVCSoft interface is necessary.) This chapter contains an
example of all four usages.
Defining MVCSoft Fault Groups
MVCSoft fault groups can be defined statically or dynamically. Static fault groups can be
built up using a convenient GUI interface, and can be “pre-instantiated” when the JDO
factory is created, thus improving runtime performance. Dynamic fault groups can be

123
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

built up in application code using simple interfaces. This is ideal when the data to be
included in the fault group is unknown until runtime.
Fault groups are always built for the base class of a particular hierarchy. This is because a
query or an object-by-id might be of the base class or any derived class. The fault group
should be defined to load fields required by any appropriate class in the hierarchy. In
circumstances where the class is known at runtime (e.g. when unloaded fields are being
loaded after an access), the fault group will be “rewritten” by the runtime to avoid
unnecessary i/o.
Defining Static Fault Groups with the GUI
To define a static fault group in the GUI, first display the list of base classes in the tree
pane by choosing the “Fault Groups” filter. In the work pane, you can select an existing
fault group using the “Edit This Fault Group” combobox. You can also add a new fault
group or delete the currently selected fault group.
A fault group’s contents are defined by moving fields or relationship folders from the left
pane to the right pane using the “Add” button. If a folder representing a relationship field
is moved without any of the fields in the target object also being moved, only the key
fields will be loaded.
You can define fault groups to any depth.
Accessing Static Fault Groups at Runtime
You can access your predefined fault groups by class and name at runtime. Cast an
instance of the javax.jdo.PersistenceManagerFactory interface to
com.mvcsoft.jdo.runtime.user.MVCSoftPersistenceManagerFactory. The method to use
is
public UserFaultGroup getFaultGroup(String className, String
groupName);
Defining Dynamic Fault Groups
You can define fault groups at runtime if the required data is not known at design time.
Acquire a dynamic fault group from the MVCSoftPersistenceManagerFactory interface,
using the following method:
public DynamicFaultGroup getDynamicFaultGroup( String forClass );
Add fields in the class using the method (the class name is required to disambiguate
identical field names in different classes in the hierarchy):
public void addSimpleField( String className, String fieldName );
Add a single or collection valued relationships using the method:
public DynamicFaultGroup addRelationshipField( String className, String
fieldName );
You can then further configure the target class in the relationship with the returned
DynamicFaultGroup.

124
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Add a map valued relationship using the method:


public DynamicFaultGroup[] addRelationshipMapField( String className,
String fieldName );
You can configure the key class using the DynamicFaultGroup located at the
DynamicFaultGroup.KEY index of the returned array. You can configure the value class
using the DynamicFaultGroup located at the DynamicFaultGroup.VALUE index of the
returned array.
The Persistence-Capable Objects for the Examples
The following six examples all use three persistence-capable objects: a customer object,
an order object, and a line item object. The relationships between the three are just what
you’d expect: a customer has many orders, and an order has many line items.
The Customer Class
The customer class has a one-many relationship with the Order class through the orders
field. The customer object has four string fields that will be used to demonstrate partial
loading of an object’s state.
package com.mvcsoft.jdosamples.fault_groups;

import java.util.Collection;

public class Customer


{
private String name;
private String address;
private String creditRating;
private String discountCode;
private Collection orders;

public Customer()
{
}

public Customer(String name, String address, String creditRating,


String discountCode, Collection orders)
{
this.name = name;
this.address = address;
this.creditRating = creditRating;
this.discountCode = discountCode;
this.orders = orders;
}

public String getName()


{
return name;
}

public void setName(String name)

125
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
this.name = name;
}

public String getAddress()


{
return address;
}

public void setAddress(String address)


{
this.address = address;
}

public String getCreditRating()


{
return creditRating;
}

public void setCreditRating(String creditRating)


{
this.creditRating = creditRating;
}

public String getDiscountCode()


{
return discountCode;
}

public void setDiscountCode(String discountCode)


{
this.discountCode = discountCode;
}

public Collection getOrders()


{
return orders;
}

public void setOrders(Collection orders)


{
this.orders = orders;
}

}
The Order Class
The order class has a one-many relationship to the LineItem class through the lineItems
collection-valued field.
package com.mvcsoft.jdosamples.fault_groups;

import java.util.Collection;

126
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public class Order


{
private String orderTaker;
private Collection lineItems;

public Order()
{
}

public Order(String orderTaker, Collection lineItems)


{
this.orderTaker = orderTaker;
this.lineItems = lineItems;
}

public String getOrderTaker()


{
return orderTaker;
}

public void setOrderTaker(String orderTaker)


{
this.orderTaker = orderTaker;
}

public Collection getLineItems()


{
return lineItems;
}

public void setLineItems(Collection lineItems)


{
this.lineItems = lineItems;
}
}
The LineItem Class
The line item class is a simple class with two values: product and quantity.
package com.mvcsoft.jdosamples.fault_groups;

public class LineItem


{
private String product;
private String quantity;

public LineItem()
{
}

public LineItem(String product, String quantity)


{
this.product = product;
this.quantity = quantity;

127
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public String getProduct()


{
return product;
}

public void setProduct(String product)


{
this.product = product;
}

public String getQuantity()


{
return quantity;
}

public void setQuantity(String quantity)


{
this.quantity = quantity;
}
}
Configuring the Example Classes
There are six configuration steps for these three classes.
1) Configure the orders field in the Customer class to be a relationship with a value
type of com.mvcsoft.jdosamples.fault_groups.Order.
2) Configure the lineItems field in the Order class to be a relationship with a value
type of com.mvcsoft.jdosamples.fault_groups.LineItem.
3) In the Table field of the Basic Info tab for the Order, change the table name. (The
default of “Order” will conflict with an SQL reserved word.
4) Using the GUI, create a total of three static fault groups. For the Customer class,
create a fault group named “info” that includes the “name” and “address” fields,
but no other fields or relationships. For the Customer class, create a second fault
group named “fieldsOrdersAndLineItems,” that includes all four Customer string
fields, the related order string field, and the two related line item string fields.
Finally, for the Order class, create a fault group named “fieldsPlusLineItems” that
includes the order string field and the two related line item string fields.
5) Configure the Customer.address field on the DB Column Configuration screen to
use the fault group “info” that you defined in step four. (Select it from the combo
box.)
6) Configure the Customer.orders relationship on the Relationship Mapping screen
to use the fault group “fieldsPlusLineItems” that you defined in step four. (Select
it from the combo box.)
Example: Refreshing Fields

128
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This example will demonstrate the use of fault groups in loading or refreshing simple
fields in the object. When a field value needs to be loaded from the database, the runtime
will use the associated fault group if one has been configured. In this example, the fault
group we associated with the address field will cause only the name and address field to
be loaded. (You can see this by monitoring the SQL that the runtime issues.)
package com.mvcsoft.jdosamples.fault_groups;

import javax.jdo.*;
import java.util.Properties;

public class DemonstrateRefreshingFields


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.fault_groups.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();

pm.currentTransaction().begin();
Customer customer = new Customer("Dan", "Schenectady", "AAA",
"7631G", null);
pm.makePersistent(customer);
pm.currentTransaction().commit();

129
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

At this point, the customer object transitions to the “Hollow” state, meaning that the Java
object represents the data in the database but the state values are not loaded into the
instance. When we call the getAddress method, the state data must be loaded. The SQL
will ask for only the name and address fields.
pm.currentTransaction().begin();
customer.getAddress();
pm.currentTransaction().commit();

}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

Example: Navigating Relationships


This example will demonstrate the use of fault groups in lazily loading relationship fields
in the object. When a single-valued relationship field is first accessed, or when a multi-
valued relationship collection class is first used, the runtime will load the related data
using the associated fault group if one has been configured. In this example, the fault
group we associated with the orders field will cause both the order objects—and their
associated line item objects—to be loaded. You can see this by monitoring the SQL that
the runtime issues. Note that, without an associated fault group, the line items for each
order would not be eagerly loaded. Note also that we don’t have any MVCSoft-specific
code—or any code related to fault groups at all.
package com.mvcsoft.jdosamples.fault_groups;

import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Arrays;

public class DemonstrateRelationshipFaultGroup

130
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.fault_groups.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();

pm.currentTransaction().begin();

Order order1 = new Order("Lynne", Arrays.asList( new LineItem[]{


new LineItem("chair", "3"),
new LineItem("couch", "1"),
new LineItem("table", "1")}
));
Order order2 = new Order("Patricia", Arrays.asList( new
LineItem[]{
new LineItem("refrigerator", "1"),
new LineItem("dishwasher", "1"),
new LineItem("television", "2")}
));
Customer customer = new Customer("Dan", "Schenectady", "AAA",
"7631G",
Arrays.asList(new Order[]{order1, order2}));
pm.makePersistent(customer);
pm.currentTransaction().commit();

131
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

pm.currentTransaction().begin();
Collection orders = customer.getOrders();
We need to actually use the collection class to trigger the load. (It is a feature of the
runtime to try to avoid unnecessary i/o by deferring it until it becomes necessary.) Note
again that we don’t have any MVCSoft-specific code—or any code related to fault
groups at all.
orders.size();
pm.currentTransaction().commit();

}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

Example: Finding an Object By ID—Static Fault Group


This example will demonstrate the use of fault groups—specified at design time but
selected dynamically at runtime—in retrieving an object by id. This technique gives you
the benefits of fault groups with maximum performance (all predefined fault groups are
instantiated once when the JDO factory is instantiated) and maximum convenience (you
can use design tools such as the MVCSoft GUI to create the fault groups).
package com.mvcsoft.jdosamples.fault_groups;

import com.mvcsoft.jdo.runtime.user.MVCSoftPersistenceManagerFactory;
import com.mvcsoft.jdo.runtime.user.UserFaultGroup;
import com.mvcsoft.jdo.runtime.user.MVCSoftPersistenceManager;

import javax.jdo.*;
import java.util.Properties;
import java.util.Arrays;

public class DemonstrateFindByIdPredefinedFaultGroup


{

132
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

private static Properties getStandardProperties()


{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.fault_groups.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager(


PersistenceManagerFactory factory)
{
return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
PersistenceManagerFactory factory =

JDOHelper.getPersistenceManagerFactory(getStandardProperties());
pm = getStandardPersistenceManager(factory);

pm.currentTransaction().begin();

Order order1 = new Order("Lynne", Arrays.asList( new LineItem[]{


new LineItem("chair", "3"),
new LineItem("couch", "1"),
new LineItem("table", "1")}
));
Order order2 = new Order("Patricia", Arrays.asList( new
LineItem[]{
new LineItem("refrigerator", "1"),
new LineItem("dishwasher", "1"),
new LineItem("television", "2")}
));
Customer customer = new Customer("Dan", "Schenectady", "AAA",
"7631G",
Arrays.asList(new Order[]{order1, order2}));
pm.makePersistent(customer);
Object oid = JDOHelper.getObjectId(customer);
pm.currentTransaction().commit();

133
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

pm.currentTransaction().begin();
// get the fault group
We cast the JDO factory instance to an instance of MVCSoftPersistenceManagerFactory
so that we can retrieve our predefined fault group by class and name.
MVCSoftPersistenceManagerFactory mvcsoftFactory =
(MVCSoftPersistenceManagerFactory) factory;
UserFaultGroup faultGroup = mvcsoftFactory.getFaultGroup(
Customer.class.getName(), "fieldsOrdersAndLineItems");
// get the object
We cast our JDO persistence manager to an instance of MVCSoftPersistenceManager so
that we can use our predefined fault group when we call getObjectById.
MVCSoftPersistenceManager mvcsoftPM =
(MVCSoftPersistenceManager) pm;
customer = (Customer) mvcsoftPM.getObjectById(oid, faultGroup);
pm.currentTransaction().commit();

}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

Example: Issuing a Query—Static Fault Group


Like the last example, this will demonstrate the use of fault groups—specified at design
time but selected dynamically at runtime—in retrieving an object, but by a query rather
than by ID.
package com.mvcsoft.jdosamples.fault_groups;

import com.mvcsoft.jdo.runtime.user.MVCSoftPersistenceManagerFactory;
import com.mvcsoft.jdo.runtime.user.UserFaultGroup;
import com.mvcsoft.jdo.runtime.user.MVCSoftQuery;

134
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Arrays;

public class DemonstrateQueryPredefinedFaultGroup


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.fault_groups.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager(


PersistenceManagerFactory factory)
{
return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
PersistenceManagerFactory factory =

JDOHelper.getPersistenceManagerFactory(getStandardProperties());
pm = getStandardPersistenceManager(factory);

pm.currentTransaction().begin();

Order order1 = new Order("Lynne", Arrays.asList( new LineItem[]{


new LineItem("chair", "3"),
new LineItem("couch", "1"),
new LineItem("table", "1")}
));
Order order2 = new Order("Patricia", Arrays.asList( new
LineItem[]{
new LineItem("refrigerator", "1"),
new LineItem("dishwasher", "1"),

135
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

new LineItem("television", "2")}


));
Customer customer = new Customer("Lauren", "Schenectady", "AAA",
"7631G",
Arrays.asList(new Order[]{order1, order2}));
pm.makePersistent(customer);
pm.currentTransaction().commit();

pm.currentTransaction().begin();
// get the fault group
MVCSoftPersistenceManagerFactory mvcsoftFactory =
(MVCSoftPersistenceManagerFactory) factory;
UserFaultGroup faultGroup = mvcsoftFactory.getFaultGroup(
Customer.class.getName(), "fieldsOrdersAndLineItems");
// get the object
Extent extent = pm.getExtent(Customer.class, true);
Query query = pm.newQuery();
query.setCandidates(extent);
query.setClass(Customer.class);
query.setFilter("name==\"Lauren\"");
We cast our JDO persistence manager to an instance of MVCSoftPersistenceManager so
that we can set the fault group to be associated with the query.
MVCSoftQuery mvcsoftQuery = (MVCSoftQuery) query;
mvcsoftQuery.setFaultGroup(faultGroup);
Collection results = (Collection) query.execute();
pm.currentTransaction().commit();

}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

Example: Finding an Object By ID—Dynamic Fault Group

136
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Sometimes you don’t know the appropriate load policy at design time, so you must
specify it dynamically at runtime. This example shows how to create a dynamic fault
group and use it when finding an object by id.
package com.mvcsoft.jdosamples.fault_groups;

import com.mvcsoft.jdo.runtime.user.MVCSoftPersistenceManagerFactory;
import com.mvcsoft.jdo.runtime.user.MVCSoftPersistenceManager;
import com.mvcsoft.jdo.runtime.user.DynamicFaultGroup;

import javax.jdo.*;
import java.util.Properties;
import java.util.Arrays;

public class DemonstrateFindByIdDynamicFaultGroup


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.fault_groups.MyGeneratedData" );
properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager(


PersistenceManagerFactory factory)
{
return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
PersistenceManagerFactory factory =

JDOHelper.getPersistenceManagerFactory(getStandardProperties());
pm = getStandardPersistenceManager(factory);

pm.currentTransaction().begin();

Order order1 = new Order("Lynne", Arrays.asList( new LineItem[]{

137
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

new LineItem("chair", "3"),


new LineItem("couch", "1"),
new LineItem("table", "1")}
));
Order order2 = new Order("Patricia", Arrays.asList( new
LineItem[]{
new LineItem("refrigerator", "1"),
new LineItem("dishwasher", "1"),
new LineItem("television", "2")}
));
Customer customer = new Customer("Dan", "Schenectady", "AAA",
"7631G",
Arrays.asList(new Order[]{order1, order2}));
pm.makePersistent(customer);
Object oid = JDOHelper.getObjectId(customer);
pm.currentTransaction().commit();

pm.currentTransaction().begin();
// create the fault group
Here’s the new code that shows how to configure a dynamic fault group. First, you get an
instance of the dynamic fault group from the MVCSoftPersistenceManagerFactory
instance.
MVCSoftPersistenceManagerFactory mvcsoftFactory =
(MVCSoftPersistenceManagerFactory) factory;
String custClassName = Customer.class.getName();
DynamicFaultGroup faultGroup =
mvcsoftFactory.getDynamicFaultGroup(
custClassName);
Add the simple fields. (You need to specify the class name in which the field is actually
defined, in the case of inheritance. This is to disambiguate identically named fields in
different classes in the hierarchy.)
faultGroup.addSimpleField(custClassName, "name");
faultGroup.addSimpleField(custClassName, "address");
faultGroup.addSimpleField(custClassName, "creditRating");
faultGroup.addSimpleField(custClassName, "discountCode");
For relationship fields, add the field and get back another instance of
DynamicFaultGroup, which you can recursively configure. Here, we add the order
relationship and add the “orderTaker” field…
DynamicFaultGroup faultGroupOrders =
faultGroup.addRelationshipField(custClassName, "orders");
String orderClassName = Order.class.getName();
faultGroupOrders.addSimpleField(orderClassName, "orderTaker");
…and here we configure the line items.
DynamicFaultGroup faultGroupLineItems =
faultGroupOrders.addRelationshipField(orderClassName,
"lineItems");
String lineItemClassName = LineItem.class.getName();
faultGroupLineItems.addSimpleField(lineItemClassName, "product");

138
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

faultGroupLineItems.addSimpleField(lineItemClassName,
"quantity");
// get the object
MVCSoftPersistenceManager mvcsoftPM =
(MVCSoftPersistenceManager) pm;
customer = (Customer) mvcsoftPM.getObjectById(oid, faultGroup);
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}

Example: Issuing a Query—Dynamic Fault Group


This example, like the last one, demonstrates the use of a dynamic fault group. This
example uses it with a query rather than with findObjectById.
package com.mvcsoft.jdosamples.fault_groups;

import com.mvcsoft.jdo.runtime.user.MVCSoftPersistenceManagerFactory;
import com.mvcsoft.jdo.runtime.user.MVCSoftPersistenceManager;
import com.mvcsoft.jdo.runtime.user.DynamicFaultGroup;

import javax.jdo.*;
import java.util.Properties;
import java.util.Arrays;

public class DemonstrateFindByIdDynamicFaultGroup


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.fault_groups.MyGeneratedData" );

139
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager(


PersistenceManagerFactory factory)
{
return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
PersistenceManagerFactory factory =

JDOHelper.getPersistenceManagerFactory(getStandardProperties());
pm = getStandardPersistenceManager(factory);

pm.currentTransaction().begin();

Order order1 = new Order("Lynne", Arrays.asList( new LineItem[]{


new LineItem("chair", "3"),
new LineItem("couch", "1"),
new LineItem("table", "1")}
));
Order order2 = new Order("Patricia", Arrays.asList( new
LineItem[]{
new LineItem("refrigerator", "1"),
new LineItem("dishwasher", "1"),
new LineItem("television", "2")}
));
Customer customer = new Customer("Dan", "Schenectady", "AAA",
"7631G",
Arrays.asList(new Order[]{order1, order2}));
pm.makePersistent(customer);
Object oid = JDOHelper.getObjectId(customer);
pm.currentTransaction().commit();

pm.currentTransaction().begin();
// create the fault group
MVCSoftPersistenceManagerFactory mvcsoftFactory =
(MVCSoftPersistenceManagerFactory) factory;
String custClassName = Customer.class.getName();

140
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

DynamicFaultGroup faultGroup =
mvcsoftFactory.getDynamicFaultGroup(
custClassName);
faultGroup.addSimpleField(custClassName, "name");
faultGroup.addSimpleField(custClassName, "address");
faultGroup.addSimpleField(custClassName, "creditRating");
faultGroup.addSimpleField(custClassName, "discountCode");
DynamicFaultGroup faultGroupOrders =
faultGroup.addRelationshipField(custClassName, "orders");
String orderClassName = Order.class.getName();
faultGroupOrders.addSimpleField(orderClassName, "orderTaker");
DynamicFaultGroup faultGroupLineItems =
faultGroupOrders.addRelationshipField(orderClassName,
"lineItems");
String lineItemClassName = LineItem.class.getName();
faultGroupLineItems.addSimpleField(lineItemClassName, "product");
faultGroupLineItems.addSimpleField(lineItemClassName,
"quantity");
// get the object
MVCSoftPersistenceManager mvcsoftPM =
(MVCSoftPersistenceManager) pm;
customer = (Customer) mvcsoftPM.getObjectById(oid, faultGroup);
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}

141
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

PersistenceManagerFactory Configuration
Properties
The PersistenceManagerFactory is—as the name implies—the factory for
PersistenceManager instances. All PersistenceManager instances are obtained from the
PersistenceManagerFactory. The factory itself is configured by specifying properties.

An instance of the factory can be obtained using the JDOHelper method


getPersistenceManagerFactory(Properties properties). In a managed
environment (e.g. an application server), you may also obtain a
PersistenceManagerFactory through a JNDI lookup.

The following properties are mandatory: the


javax.jdo.PersistenceManagerFactoryClass property (which should always have a
value of com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory if
you are using the MVCSoft product), the com.mvcsoft.jdo.MetaDataClass property
(which should have the value of your metadata class name), and some combination of
properties that will allow the runtime to access the database.
Standard Properties Defined by the JDO Specification
The following standard properties are defined by the JDO specification. Their use is
portable between JDO products.
javax.jdo.PersistenceManagerFactoryClass
This class identifies the implementation class provided by your JDO vendor. For
MVCSoft, it is
com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory.
javax.jdo.option.Optimistic
There are two types of transactions defined by the JDO specification: optimistic or data
store. If this value is set in the PersistenceManagerFactory, it controls the default
transaction type for all subsequently created PersistenceManager instances. Legal values
are “true” for optimistic-by-default, and “false” for data-store-by-default.
javax.jdo.option.RetainValues
If this value is set in the PersistenceManagerFactory, it controls the default setting for all
subsequently created PersistenceManager instances. Legal values are “true” so that object
state is mantained after the transaction successfully completes, and “false” so that object
state is cleared on transacton completion.
javax.jdo.option.RestoreValues

142
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

If this value is set in the PersistenceManagerFactory, it controls the default setting for all
subsequently created PersistenceManager instances. Legal values are “true” so that object
state is mantained after the transaction is rolled back, and “false” so that object state is
cleared on transacton rollback.
javax.jdo.option.IgnoreCache
If this value is set in the PersistenceManagerFactory, it controls the default setting for all
subsequently created PersistenceManager instances. Legal values are “true” if it is ok to
ignore changes made to objects within the current transaction when issuing the query
(which allows you to avoid a database flush), and “false” if the modified object state must
be considered.
javax.jdo.option.NontransactionalRead
If this value is set in the PersistenceManagerFactory, it controls the default setting for all
subsequently created PersistenceManager instances. Legal values are “true” if it should
be possible to read data outside of a transaction, and “false” if it should not be allowed by
the runtime.
javax.jdo.option.NontransactionalWrite
If this value is set in the PersistenceManagerFactory, it controls the default setting for all
subsequently created PersistenceManager instances. The MVCSoft JDO Toolkit does not
support non-transactional writes.
javax.jdo.option.Multithreaded
If this value is set in the PersistenceManagerFactory, it controls the default setting for all
subsequently created PersistenceManager instances. Legal values are “true” if the
application will access persistence instances associated with one PersistenceManager
from multiple threads, and “false” if a single thread will be used per PersistenceManager
and associated persistence capable objects.
javax.jdo.option.ConnectionUserName
If the MVCSoft runtime will be managing connections itself (rather than relying on an
application server or other managed environment), this is the name of the user to be
supplied by the runtime when establishing a connection.
javax.jdo.option.ConnectionPassword
If the MVCSoft runtime will be managing connections itself (rather than relying on an
application server or other managed environment), this is the password to be supplied by
the runtime when establishing a connection.
javax.jdo.option.ConnectionURL
If the MVCSoft runtime will be managing connections itself (rather than relying on an
application server or other managed environment), this is the database URL to be used by
the runtime when establishing a connection.

143
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

javax.jdo.option.ConnectionDriverName
If the MVCSoft runtime will be managing connections itself (rather than relying on an
application server or other managed environment), this is the class name of the JDBC
driver to be used by the runtime when establishing a connection.
javax.jdo.option.ConnectionFactoryName
If the MVCSoft runtime will be relying on an application server or other managed
environment, this is the JNDI name by which a connection factory is looked up. This
must be the name of a connection factory that will return a connection enlisted in
the thread’s current transaction. Consult the documentation for your application server
for instructions on how to do this.
javax.jdo.option.ConnectionFactory2Name
If the MVCSoft runtime will be relying on an application server or other managed
environment, this is the JNDI name by which a connection factory is looked up. This
must be the name of a connection factory that will return a connection not enlisted in the
thread’s current transaction. Consult the documentation for your application server for
instructions on how to do this. This connection is used for optimistic transaction
management and acquisition of blocks of IDs.
MVCSoft-specific Properties
Immediately following is a list of properties specific to the base MVCSoft product.

In addition to the following properties, there are properties specific to


cache integration modules provided with the MVCSoft product. These are
documented in the chapter on 2nd level caches.
If you write a cache integration module, you may also use custom factory
configuration properties. If your cache integration module implements
com.mvcsoft.jdo.runtime.user.FactoryInitialization, these properties will
be passed via the FactoryInitialization.init( Properties properties,
MetaData metaData) method call.

com.mvcsoft.jdo.MetaDataClass
The MVCSoft JDO Toolkit takes the application metadata stored in one or more .jdo
files, and turns it into a single Java class file. This is the fully-qualified class name of this
metadata class. (You choose a name for the class and create it using the MVCSoft tools,
either the GUI or the Ant tasks.)
com.mvcsoft.jdo.LogClassFactory
It will often be the case that you would like to log the database i/o produced by the JDO
runtime. You can use any logging system you’d like for this purpose—anything from
printing to the console to using a system like log4j. The logger you design must

144
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

implement the following MVCSoft interface (which can be a simple wrapper over
another logging system):
package com.mvcsoft.jdo.runtime.user;

public interface Logger


{
public boolean isInfoEnabled();
public void info(String val);
public void error(String val);
public void error(String val, Exception e);
}
MVCSoft provides three implementations of this class (though you can easily write your
own): a logger that issues System.out.println statements (recommended only for
debugging); a wrapper to Apache’s log4j (the best choice for production systems); and a
“null logger” that does not enable info logging and consumes error messages.
The runtime obtains the Logger class from a LoggerFactory. This allows statements and
statement parameters to be logged to different loggers. The factory class you design must
implement the following interface:
package com.mvcsoft.jdo.runtime.user;
public interface LoggerFactory
{
public Logger getStatementLogger(String configuredName);
public Logger getParameterLogger(String configuredName);
}
MVCSoft provides three implementations of the LoggerFactory, corresponding to the
three implementations of Logger: com.mvcsoft.jdo.runtime.util.LoggerFactoryConsole,
com.mvcsoft.jdo.runtime.util.LoggerFactoryLog4J, and
com.mvcsoft.jdo.runtime.util.LoggerFactoryNull. (The default is
LoggerFactoryConsole.) You can pass any of these fully qualified class names, or a class
name of your own factory instance, as the parameter associated with
com.mvcsoft.jdo.LogClassFactory. (The configuredName parameter is provided by one
of the following two properties. It may or may not be used by the LoggerFactory
instance; of the three MVCSoft-provided classes, only the LoggerFactoryLog4J instance
uses these names.)
com.mvcsoft.jdo.ParameterLoggerName
This parameter may be used by the LoggerFactory when the Logger for the parameters of
an SQL statement is retrieved. The MVCSoft-provided class
com.mvcsoft.jdo.runtime.util.LoggerFactoryLog4J uses this name in the call
org.apache.log4j.Logger.getLogger(), as the parameter to the getLogger() method.
com.mvcsoft.jdo.StatementLoggerName
This parameter may be used by the LoggerFactory when the Logger for an SQL
statement is retrieved. The MVCSoft-provided class

145
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

com.mvcsoft.jdo.runtime.util.LoggerFactoryLog4J uses this name in the call


org.apache.log4j.Logger.getLogger(), as the parameter to the getLogger() method.
com.mvcsoft.jdo.ManagedEnvironment
This boolean parameter indicates whether or not the runtime is executing in a managed
environment such as an application server. If the ManagedEnvironment parameter is
“true,” the following three changes to the JDO runtime are in effect:
♦ The javax.jdo.Transaction methods begin(), commit(), and rollback() are tied to the
Transaction instance in the managed environment. With EJBs, transaction boundaries
are managed by the application server in the case of container-managed transactions.
With bean-managed transactions, calls to the JDO transaction instance’s begin(),
commit(), and rollback() methods are propagated to a UserTransaction instance.
♦ Rather than managing transactional caches and callbacks directly, the runtime
retrieves the javax.transaction.Transaction instance from the application server, using
its implementation of hashcode and equals, and registering a synchronization object
with it for transactional callbacks.
♦ Within a transaction, two different calls to
PersistenceManagerFactory.getPersistenceManager will return the same
PersistenceManager instance.
javax.jdo.option.MinPool
If specified, the MinPool value should be an integer equal to or greater than zero. If either
the MinPool or MaxPool value is non-null (and a datasource is not specified), the
MVCSoft runtime pools connections. (In the current release, the MinPool value is not
actually used by the connection pool.)
If a datasource is specified, connection pooling is deferred to that datasource regardless
of this setting.
javax.jdo.option.MaxPool
If specified, the MaxPool value should be an integer equal to or greater than zero. If
either the MinPool or MaxPool value is non-null (and a datasource is not specified), the
MVCSoft runtime pools connections. If a MinPool value is specified but no MaxPool
value is specified, the MaxPool is the greater of the MinPool and 5. (You should specify
a MaxPool value if you are going to use connection pooling.) Internally, the MVCSoft
runtime makes use of Apache Commons connection pooling.
If a datasource is specified, connection pooling is deferred to that datasource regardless
of this setting.
com.mvcsoft.jdo.TransactionRetrieverClass
In a managed environment, the JDO runtime uses the application server’s
javax.transaction.Transaction instance rather than its own. There is no standard for

146
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

retrieving this instance, so the runtime behavior to do so is encapsulated behind the


following interface:
package com.mvcsoft.jdo.runtime.user;

import javax.transaction.Transaction;

public interface TransactionRetriever


{
public Transaction getCurrentTransaction();
}
To use the MVCSoft runtime in a new managed environment, you can write your own
implementation of TransactionRetriever and provide the class name using this property.
Usually the Transaction instance will be available from the
javax.transaction.TransactionManager instance, which itself is available from the JNDI
namespace. MVCSoft provides a class that implements this behavior:
com.mvcsoft.jdo.runtime.util.JNDITransactionRetriever (the default for this property). It
works in conjunction with the com.mvcsoft.jdo.TransactionManagerJNDIName property.
For application servers that provide the TransactionManager in the JNDI
namespace, all you will need to do is provide the TransactionManagerJNDIName.
com.mvcsoft.jdo.TransactionManagerJNDIName
This property works in conjunction with the MVCSoft-supplied TransactionRetriever
implementation com.mvcsoft.jdo.runtime.util.JNDITransactionRetriever (which is the
default). Its value should be the JNDI name of the javax.transaction.TransactionManager
instance for your application server. Some common values:
♦ JBoss: java:/TransactionManager
♦ JRun: java:/TransactionManager
♦ Orion: java:comp/UserTransaction
♦ OC4J: java:comp/UserTransaction
♦ WebLogic: javax/transaction/TransactionManager
com.mvcsoft.jdo.RollbackStrategy
The JTA specification does not describe how to indicate an error condition in a
beforeCompletion callback from a TransactionManager. The MVCSoft JDO Toolkit
allows you to choose between two strategies: Exception and RollbackOnly. The
Exception strategy marks the transaction for rollback and throws an exception. The
RollbackOnly strategy (the default) marks the transaction for rollback, and logs but does
not throw the exception.
com.mvcsoft.jdo.FunctionMap
The JDO specification defines only three methods for its query filters: startsWith(),
endsWith(), and isEmpty() for collections only. Rather than define a limited set of

147
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

additional methods, the MVCSoft JDO toolkit allows you to define your own methods
and their translation into SQL.See the chapter on queries for a detailed explanation of
function maps, with examples.
Your custom methods are defined in a properties file. The format, explained in the
chapter on JDO queries, is fundamentally a series of entries where the method
specification = sql translation conversion is specified.
This properties file needs to be available to the JDO class loader at runtime. In other
words, it should either be stored in a JAR or directory on the same classpath as your
persistence capable classes, or it should be available to the ContextClassLoader of the
thread that is initializing the PersistenceManagerFactory. Specify the name of the
function map here. The MVCSoft runtime will load it using getResourceAsStream.
com.mvcsoft.jdo.CacheClass
The class—if any—that implements com.mvcsoft.jdo.runtime.cache.Cache and will be
used for level two caching. MVCSoft currently supplies one default implementation of
this class which provides for a per-PersistenceManagerFactory read-write cache. (This
cache implementation can be trivially wrapped in a user-supplied class to provide a per-
VM cache.) See the chapter on level-two caching for more information.
com.mvcsoft.jdo.CachePolicyClass
The class—if any—that implements com.mvcsoft.jdo.runtime.cache.CachePolicy. This
class provides information to the runtime about which classes and fields should actually
be cached. MVCSoft currently supplies one implementation of this class that will use a
properties file to read class and field policies:
com.mvcsoft.jdo.runtime.cache.util.CachePolicyProperties. See the chapter on level-two
caching for more information.
com.mvcsoft.jdo.cache.max.size
This property is read by the com.mvcsoft.jdo.runtime.cache.Cache class, and configures
the maximum number of objects stored in the cache at runtime.
com.mvcsoft.jdo.cache.policy.properties
This property is read by the com.mvcsoft.jdo.runtime.cache.util.CachePolicyProperties
class, and configures the fully-qualified resource name of the properties file used by that
policy class. It is loaded using ClassLoader.getResourceAsStream() from the JDO
classloader.
com.mvcsoft.jdo.sql.dialect
This property determines how left joins are done, either with “left join on…” syntax, or
Oracle’s proprietary where x=y(+) syntax. Valid values are standard or oracle. Users
targeting Oracle database versions that support ANSI standard “left join on…” syntax—
version 9i or greater—should use the standard setting rather than the oracle setting.

148
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

com.mvcsoft.jdo.sql.CreateFkConstraints
This boolean property determines whether or not DDL includes creating foreign key
constraints. The default is false.
com.mvcsoft.jdo.sql.CreateIndexes
This boolean property determines whether or not DDL includes creating indexes. The
default is true.
com.mvcsoft.jdo.sql.CreateIndexTemplate
This is the template for creating indexes. Values are substituted at runtime as follows:
♦ constraintName ?1
♦ table ?2
♦ field ?3
♦ uniqueModifier ?4
If it is not specified, the following template is used: create ?4 index ?1 on ?2 ( ?3 )
com.mvcsoft.jdo.sql.DropIndexTemplate
This is the template for dropping indexes. Values are substituted at runtime as follows:
♦ constraintName ?1
♦ table ?2
♦ field ?3
♦ uniqueModifier ?4
If it is not specified, the following template is used: drop index ?2.?1
com.mvcsoft.jdo.sql.CreateFkTemplate
This is the template for creating foreign keys. Values are substituted at runtime as
follows:
♦ table1 ?1
♦ constraintName ?2
♦ table1Keys ?3
♦ table2 ?4
♦ table2Keys ?5
If it is not specified, the following template is used: alter table ?1 add constraint ?2
foreign key (?3) references ?4 (?5)
com.mvcsoft.jdo.sql.DropFkTemplate

149
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This is the template for dropping foreign keys. Values are substituted at runtime as
follows:
♦ table1 ?1
♦ constraintName ?2
♦ table1Keys ?3
♦ table2 ?4
♦ table2Keys ?5
If it is not specified, the following template is used: alter table ?1 drop constraint ?2
com.mvcsoft.jdo.sql.CreateTableTemplate
This is the template for creating tables. Values are substituted at runtime as follows:
♦ name ?1
♦ fields ?2
♦ key ?3
If it is not specified, the following template is used: create table ?1 (?2, primary key (?3))
com.mvcsoft.jdo.sql.DropTableTemplate
This is the template for dropping tables. Values are substituted at runtime as follows:
♦ name ?1
♦ fields ?2
♦ key ?3
If it is not specified, the following template is used: drop table ?1
com.mvcsoft.jdo.sql.DeleteAllFromTableTemplate
This is the template for deleting all the data from a table prior to dropping it. Values are
substituted at runtime as follows:
♦ name ?1
♦ fields ?2
♦ key ?3
If it is not specified, the following template is used: delete from ?1
Properties for the Schema Update Tool
See the chapter on the schema update tool for more information about the following
properties.
com.mvcsoft.jdo.sql.AlterTableRenameTemplate

150
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

com.mvcsoft.jdo.sql.AlterTableAddColumnTemplate
com.mvcsoft.jdo.sql.AlterTableRenameColumnTemplate
com.mvcsoft.jdo.sql.AlterTableDropColumnTemplate
com.mvcsoft.jdo.sql.AlterTableChangeColumnTypeTemplate
com.mvcsoft.jdo.sql.AlterTableChangeColumnNameAndTypeTemplate
com.mvcsoft.jdo.sql.AlterTableAddLockFieldTemplate
com.mvcsoft.jdo.sql.AlterTableAddDiscriminatorFieldTemplate
com.mvcsoft.jdo.sql.AlterTableDropPrimaryKey
com.mvcsoft.jdo.sql.AlterTableAddPrimaryKey
com.mvcsoft.jdo.sql.MoveRelationshipToFk
com.mvcsoft.jdo.sql.MoveRelationshipToTable

151
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Configuration File Formats


Type-Mappings Configuration
The type-mappings file format is used with the com.mvcsoft.jdo.user.DefaultTypes
implementation class com.mvcsoft.jdo.generator.defaults.XmlDefaultTypes. It
provides default field-to-column mappings based on the Java type of the field. The
typical usage is to provide a default mapping for a particular target database. In the GUI,
you place this file in the config directory and can then select it for a particular project.
With the Ant task, you provide it as a parameter. You can also specify a second file that
overrides particular types in the first file. This functionality is simply a convenience if
one project or target database needs defaults only slightly different from another.
DTD Reference
<!ELEMENT type-mappings (description?, type-mapping*, wildcard*,
discriminator-sql-type? )>

The type-mappings element is the root element.

<!ELEMENT type-mapping (java-type, jdbc-type?, prepared-statement-


method?, result-set-method?,sql-type?, data-transformer?, mapping-
transformer?, constant-transformer?)>

A type-mapping is a single Java-type-to-mapping-information entry. All


the elements except for java-type are optional. If an element isn’t
specified in a type map being used as an “override,” it will be picked
up from the map it is overriding. If an element isn’t specified in a
basic type map, the runtime will provide a hard-coded reasonable
default based on the Java type.

<!ELEMENT wildcard (description?, pattern, substitution)>


Wildcards are used to translate SQL types provided in the configuration
of a single field to a type appropriate to a particular database. For
instance, you might configure a “quantity” field in a customer
persistence capable object to be of type INTEGER. A target database
might not support this datatype. You could specify a wildcard mapping
to map INTEGERs to DECIMAL(10)s when you generate the metadata for that
database target (rather than changing all the relevant fields).

<!ELEMENT pattern (#PCDATA)>


The SQL type pattern specified for the field. (See the entry for the
wildcard element.)

<!ELEMENT substitution (#PCDATA)>


The SQL type to actually use. (See the entry for the wildcard element.)

152
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

<!ELEMENT description (#PCDATA)>


Used to provide text describing the parent element.

<!ELEMENT java-type (#PCDATA)>


The Java type for which this mapping applies. Primitive types are
considered different from their wrappers, so int and java.lang.Integer
have different entries.

<!ELEMENT sql-type (#PCDATA)>


The corresponding default sql type for a java type.

<!ELEMENT jdbc-type (#PCDATA)>


The corresponding default jdbc type for a java type. See elsewhere in
the documentation for more details.

<!ELEMENT prepared-statement-method (#PCDATA)>


The corresponding default prepared statement method for a java type.
See elsewhere in the documentation for more details.

<!ELEMENT result-set-method (#PCDATA)>


The corresponding default result set method for a java type. See
elsewhere in the documentation for more details.

<!ELEMENT data-transformer (#PCDATA)>


The corresponding default data transformer for a java type. See
elsewhere in the documentation for more details.

<!ELEMENT mapping-transformer (#PCDATA)>


The corresponding default mapping transformer for a java type. See
elsewhere in the documentation for more details.

<!ELEMENT constant-transformer (#PCDATA)>


The corresponding default constant transformer for a java type. See
elsewhere in the documentation for more details.

153
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Transactional Options
The JDO specification provides for four areas of configuration for transactional behavior:
non-transactional access to persistent behavior;; retain values after commit; restore values
after rollback; and optimistic concurrency control. Particular options within these
categories are not required by the JDO specification and may or may not be available for
a particular implementation. The MVCSoft JDO Toolkit supports every option but non-
transactional write (which is not well suited to a relational database back-end).
You can configure these options using methods on an instance of javax.jdo.Transaction,
which as we have seen earlier, you acquire by calling currentTransaction() on an instance
of javax.jdo.PersistenceManager. The defaults for these options can be set when
configuring the PersistenceManagerFactory.
Non-transactional Access to Persistent Behavior
There are two types of non-transactional access: reads and writes.
MVCSoft supports non-transactional reads. If this flag is set to true (either by default or
through the Transaction.setNontransactionalRead method, the runtime will allow queries
and the navigation of relationships outside of an active transaction.. If this flag is set to
false, then queries and relationship navigation outside of an active transaction causes a
JDOUserException to be thrown.
MVCSoft does not support non-transactional writes. Updates to non-transactional
instances outside of an active transaction throw a JDOUserException.
Retain Values after Commit
If this flag is set to true (either by default from the PersistenceManagerFactory
configuration or through the Transaction.setRetainValues() method), the state of the
object will be saved after the transaction commits, and the object will be considered
“persistent non-transactional.” That state might be used during a subsequent transaction,
or during a non-transactional read, or after a call to PersistenceManager.makeTransient.
Restore Values after Rollback
If this flag is set to true (either by default from the PersistenceManagerFactory
configuration or through the Transaction.setRestoreValues() method), the field values are
restored to their values at the start of the transaction for fields that are primitive types,
wrapper types, immutable types, and references to persistence-capable types. Fields of
mutable types are set to null.
Optimistic Concurrency Control
A database will maintain a transaction’s A.C.I.D. properties (atomic, consistent, isolated
durable), but the cost of this is often an unacceptable reduction in concurrency. The JDO

154
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

specification provides for—and MVCSoft supports—an alternative to “data store


transactions.” This is optimistic concurrency control.
In optimistic concurrency control, data is read and written using many short database
transactions, rather than a single overarching database transaction corresponding to the
JDO transaction. The advantage of this is that data you read or write is not locked for the
duration of the JDO transaction, and can be used by other, concurrent transactions. The
disadvantage is that the database is no longer preventing conflicting updates.
When optimistic concurrency control is used, the JDO runtime is responsible for ensuring
that no more than one of a set of conflicting transactions commits. This is done by
verifying the object state against the database state. The verification mechanism can be
configured for each persistence-capable class, and is described elsewhere in this
documentation. (The basic strategies are: compare fields, counter, and timestamp.) These
checks—where configured—are applied automatically when an object is updated or
deleted. Individual objects that have only been read still can be included in the
verification, by making them transactional. To make an object transactional during an
optimistic transaction, call the method PersistenceManager.makeTransactional.
Code Examples
This section presents a code example for each transactional option listed above: non-
transactional access to persistent behavior; retain values after commit; restore values after
rollback; and optimistic concurrency control. All these code samples use the following
persistence capable object:
package com.mvcsoft.jdosamples.transactions.optimistic;

public class ObjToLock


{
private String val1;
private String val2;
private String val3;

public ObjToLock()
{
}

public ObjToLock(String val1, String val2, String val3)


{
this.val1 = val1;
this.val2 = val2;
this.val3 = val3;
}

public String getVal1()


{
return val1;
}

public void setVal1(String val1)

155
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
this.val1 = val1;
}

public String getVal2()


{
return val2;
}

public void setVal2(String val2)


{
this.val2 = val2;
}

public String getVal3()


{
return val3;
}

public void setVal3(String val3)


{
this.val3 = val3;
}
}
Configure this object to use a locking type of counter. If you do not specify some sort of
optimistic locking mechanism for this object, the optimistic locking example will not
work.
Sample: Non-transactional Access to Persistent Behavior
This sample demonstrates two scenarios. In the first, we set the non-transactional read
setting to true. We then issue a query without starting a transaction. This works without
problem. We then set the non-transactional read setting to false. When we try to issue the
same query without starting a transaction, we get an (expected) exception.
package com.mvcsoft.jdosamples.transactions.optimistic;

import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Iterator;

public class NonTransAccessMain


{
public static void main(String[] args)
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",

"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.transactions.optimistic.MyMetadata");

156
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(properties);
PersistenceManager pm = factory.getPersistenceManager();
Transaction transaction=null;

try
{
Here we’re just creating an object to use in the tests:
// let's create an object
transaction = pm.currentTransaction();
ObjToLock newObject = new
ObjToLock("nonTranStr1","nonTranStr1","nonTranStr1");
transaction.begin();
pm.makePersistent(newObject);
transaction.commit();

Now we try the first scenario—with non-transactional read set to true. This succeeds.
// here we'll see a non-transactional read, this should work
try
{
transaction.setNontransactionalRead(true);
Query query = pm.newQuery();
query.setCandidates(pm.getExtent(ObjToLock.class, true));
query.setClass(ObjToLock.class);
query.setFilter("val1==\"nonTranStr1\"");
Collection collection = (Collection)query.execute();
for (Iterator iterator = collection.iterator();
iterator.hasNext();)
{
ObjToLock retrieved = (ObjToLock) iterator.next();
String val1 = retrieved.getVal1();
}
System.out.println("No exception expected");
}
catch (JDOException jdoe)
{
System.out.println("Error: unexpected exception");
}

try
{

157
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Now we try the first scenario—with non-transactional read set to false. This fails.
// here we'll see a non-transactional read, this should throw
an exception
transaction.setNontransactionalRead(false);
Query query = pm.newQuery();
query.setCandidates(pm.getExtent(ObjToLock.class, true));
query.setClass(ObjToLock.class);
query.setFilter("val1==\"nonTranStr1\"");
Collection collection = (Collection)query.execute();
for (Iterator iterator = collection.iterator();
iterator.hasNext();)
{
ObjToLock retrieved = (ObjToLock) iterator.next();
String val1 = retrieved.getVal1();
System.out.println(val1);
}
System.out.println("Error: exception didn't occur");
}
catch (JDOException jdoe)
{
System.out.println("Expected exception:");
jdoe.printStackTrace();
}

}
catch (Exception e)
{
e.printStackTrace();
try
{
if (transaction != null)
transaction.rollback();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}

158
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

}
Sample: Retain Values After Commit
This sample demonstrates two scenarios. In the first, the retain values setting is false.
After the transaction in which we create a sample object completes, the values are null.
The object is in the state known in the JDO specification as Hollow. Only the primary
key is still associated with the Java instance.
In the second scenario, the retain values setting is true. After the transaction in which we
create a sample object completes, the values are retained in the object. We access them
by making the instance transient (i.e. unmanaged by the JDO runtime). An alternative
would be to allow non-transactional reads. If we simply tried to read the value without
either of these two steps, the runtime would throw an exception complaining about an
illegal non-transactional read.
package com.mvcsoft.jdosamples.transactions.optimistic;

import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Iterator;

public class RetainValuesMain


{
public static void main(String[] args)
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",

"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.transactions.optimistic.MyMetadata");

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(properties);
PersistenceManager pm = factory.getPersistenceManager();
Transaction transaction=null;

try
{

159
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

transaction = pm.currentTransaction();

In this first scenario, the values are set to null by the runtime.
// let's create an object without retaining values
ObjToLock newObject1 = new
ObjToLock("retainStr1","retainStr1","retainStr1");
transaction.setRetainValues(false);
transaction.begin();
pm.makePersistent(newObject1);
transaction.commit();
If we didn’t make the object transient here, the next line of code would throw an
exception.
pm.makeTransient(newObject1);

if (newObject1.getVal1() == null)
System.out.println("Worked as expected");
else
System.out.println("Value unexpectedly retained");

In the second scenario, we ask the runtime to retain the values.


// let's create an object and retain its values
transaction = pm.currentTransaction();
ObjToLock newObject2 = new
ObjToLock("retainStr2","retainStr2","retainStr2");
transaction.setRetainValues(true);
transaction.begin();
pm.makePersistent(newObject2);
transaction.commit();
pm.makeTransient(newObject2);

if ("retainStr2".equals(newObject2.getVal1()))
System.out.println("Worked as expected");
else
System.out.println("Value unexpectedly not retained");

}
catch (Exception e)
{
e.printStackTrace();
try
{
if (transaction != null)
transaction.rollback();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
finally

160
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}

}
Sample: Restore Values After Rollback
The previous example showed how to control what happens to an object’s managed state
when the transaction commits. This example shows how to control what happens to an
object’s managed state when the transaction rolls back. Otherwise, the two examples are
identical.
package com.mvcsoft.jdosamples.transactions.optimistic;

import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Iterator;

public class RestoreValuesMain


{
public static void main(String[] args)
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",

"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.transactions.optimistic.MyMetadata");

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

161
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(properties);
PersistenceManager pm = factory.getPersistenceManager();
Transaction transaction=null;

try
{
transaction = pm.currentTransaction();

// let's create an object to use as a test case


ObjToLock theObject = new
ObjToLock("restoreStr1","restoreStr1","restoreStr1");
transaction.setRetainValues(false);
transaction.begin();
pm.makePersistent(theObject);
Object oid = JDOHelper.getObjectId(theObject);
transaction.commit();

In this first scenario with the restore values property set to false, the object state will be
set to null when the transaction rolls back.
// let's see what happens when we roll back a transaction with
restore==false
transaction.setRestoreValues(false);
transaction.begin();
theObject = (ObjToLock) pm.getObjectById(oid, true);
theObject.setVal1("restoreStr2");
transaction.rollback();
pm.makeTransient(theObject);
if (theObject.getVal1() == null)
System.out.println("Worked as expected");
else
System.out.println("Value unexpectedly retained or restored");

In this second scenario, the object state will be restored when the transaction rolls back.
// now try a transaction with restore==true
transaction.setRestoreValues(true);
transaction.begin();
theObject = (ObjToLock) pm.getObjectById(oid, true);
theObject.setVal1("restoreStr2");
transaction.rollback();
pm.makeTransient(theObject);
if ("restoreStr1".equals(theObject.getVal1()))
System.out.println("Worked as expected");
else
System.out.println("Value unexpectedly *not* restored");

}
catch (Exception e)
{
e.printStackTrace();
try

162
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
if (transaction != null)
transaction.rollback();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}

}
Sample: Optimistic Concurrency Control
This sample demonstrates concurrent transactions experiencing a conflict, with the result
that one of the transactions is rolled back. An interesting twist is that the object whose
verification fails was only read, not modified in the rolled back transaction. By default
only modified or deleted instances will be verified when an optimistic transaction tries to
commit. However, we enlisted the read-only instance in the transaction by calling the
PersistenceManager.makeTransactional method.
The scenario is simple. We:
1) Create two test objects
2) Read the objects in an optimistic transaction (T1)
3) Use a second transaction (T2) to concurrently modify one of the objects
4) Commit T2
5) Try to commit T1. The runtime will detect the concurrent modification and throw
an exception.
package com.mvcsoft.jdosamples.transactions.optimistic;

import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;

163
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import java.util.Properties;

public class LockTestVerifyMain


{
public static void main(String[] args)
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",

"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.transactions.optimistic.MyMetadata");

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(properties);
PersistenceManager pm1 = factory.getPersistenceManager();
PersistenceManager pm2 = factory.getPersistenceManager();

Transaction T2 = pm1.currentTransaction();
Transaction T1 = pm2.currentTransaction();
Notice that we set T1 to be optimistic here. (We don’t need to do this for T2 in this
example.)
T1.setOptimistic(true);
try
{
Object oidToWrite = null;
Object oidToRead = null;

Step one: Create two test objects


// create a couple of objects
T2.begin();
ObjToLock objToWrite = new ObjToLock("a1", "b1", "c1");
ObjToLock objToRead = new ObjToLock("aa1", "bb1", "cc1");
pm1.makePersistent(objToWrite);
pm1.makePersistent(objToRead);
oidToWrite = JDOHelper.getObjectId(objToWrite);
oidToRead = JDOHelper.getObjectId(objToRead);
T2.commit();

164
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Step two: Read the objects in an optimistic transaction (T1)


// read the objects in a transaction
T1.begin();
ObjToLock objToWrite2 = (ObjToLock) pm2.getObjectById(oidToWrite,
true);
ObjToLock objToRead2 = (ObjToLock) pm2.getObjectById(oidToRead,
true);

Step three: Use a second transaction (T2) to concurrently modify one of the objects
// during a concurrent transaction, we change the object that
// is only being *read* (not written) in the above transaction
T2.begin();
objToRead = (ObjToLock) pm1.getObjectById(oidToRead, true);
objToRead.setVal1("new");
Step four: Commit T2
T2.commit();

Add this object to the list of objects to verify. (Updated or deleted objects are
automatically verified.)
// here we are saying "please validate
pm2.makeTransactional(objToRead2);

Step five: Try to commit T1. The runtime will detect the concurrent modification and
throw an exception.
objToWrite2.setVal2("b2");
T1.commit();

}
catch (Exception e)
{
e.printStackTrace();
try
{
if (T2 != null)
T2.rollback();
}
catch (Exception e1)
{
e1.printStackTrace();
}
try
{
if (T1 != null)
T1.rollback();
}
catch (Exception e1)
{
e1.printStackTrace();

165
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

}
}
finally
{
try
{
if (pm1.currentTransaction().isActive())
pm1.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm1.close();
}

166
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Ant and XDoclet


MVCSoft provides Ant tasks so that persistence-capable classes can be enhanced and
your metadata class generated as part of a productive build environment. These Ant tasks
can be used with .jdo files produced with the MVCSoft GUI or by the MVCSoft XDoclet
support.
There are three Ant tasks provided by MVCSoft. The first allows you to enhance classes
in conformance with the JDO specification. The second allows you to generate a .java file
for the metadata class (which provides the information in the .jdo files to the MVCSoft
JDO runtime). The third allows you to create or remove the tables required for JDO
persistence to your target database.
The MVCSoft XDoclet support allows you to configure your JDO metadata using a
specialized MVCSoft JDO support sub-module, and to generate lists of persistence-
capable and persistence-aware classes.
Ant Tasks
The three Ant tasks are EnhanceClasses, GenerateMetaData, and SchemaDDL.
EnhanceClasses
The class name of the Ant task to enhance classes is
com.mvcsoft.jdo.ant_tasks.EnhanceClasses. You would set up an Ant taskdef with
something like this:
<taskdef
name="enhance"
classname="com.mvcsoft.jdo.ant_tasks.EnhanceClasses"
classpathref="project.class.path"
/>
The options for the task are as follows:
persistenceCapableClassList
This is the file name of a list of new-line-separated fully-qualified names of classes to
enhance as persistence capable. The list might look like this:
busobjs.simple_hierarchies.B_Class
busobjs.simple_hierarchies.A_Class
busobjs.types.BasicTypes
busobjs.maptests.MapAppKeyParent
busobjs.simple_relationships.basic.SimpleParent
busobjs.simple_relationships.basic.SimpleChild
persistenceAwareClassList
This is the file name of a list of new-line-separated fully-qualified names of classes to
enhance as persistence aware.

167
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

outputDirectory
This mandatory parameter is the directory to which the enhanced classes are output.
classpath
This is the classpath from which classes to be enhanced (and their .jdo metadata) are
loaded.
metadataLevel
The .jdo metadata is loaded from the classpath. It will have been stored at the class,
package, or project level, which can be specified here as “class,” “package,” or “project.”
The default is “package.”
metadataFileName
If the .jdo metadata has been stored at the “project” level, this is the file name of the .jdo
file.
You would use the EnhanceClasses task with something like this:
<enhance
persistenceCapableClassList="${basedir}/${build.dir}/persistence_capabl
e.txt"

persistenceAwareClassList="${basedir}/${build.dir}/persistence_aware.tx
t"
outputDirectory="${basedir}/${build.dir}">
<classpath path="${basedir}/${build.dir}" />
</enhance>

GenerateMetaData
The class name of the Ant task to generate the .java metadata file is
com.mvcsoft.jdo.ant_tasks.GenerateMetaData. You would set up an Ant taskdef with
something like this:
<taskdef
name="genmetadata"
classname="com.mvcsoft.jdo.ant_tasks.GenerateMetaData"
classpathref="project.class.path"
/>
The options for the task are as follows:
generatedPackageName
This is the package name of the generated metadata class.
generatedClassName

168
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This is the simple class name of the generated metadata class. (The fully-qualified name
is the generatedPackageName + “.” + generatedClassName.)
defaultNames
This is the fully-qualified name of the user-provided implementation of DefaultNames.
(The default is the MVCSoft-supplied class
com.mvcsoft.jdo.generator.defaults.StandardDefaultNames.)
defaultTypes
This is the fully-qualified name of the user-provided implementation of DefaultTypes.
(The default is the MVCSoft-supplied class
com.mvcsoft.jdo.generator.defaults.XmlDefaultTypes.)
defaultTypesMap
This is the file name of a types map for default types targeted to a specific database. It is
used in conjunction with the XmlDefaultTypes class.
defaultTypesOverride
This is the file name of a types map for default types targeted to a specific database. It is
used in conjunction with the XmlDefaultTypes class, and overrides the values provided
by the file specified in defaultTypesMap.
metadataLevel
The .jdo metadata is loaded from the classpath. It will have been stored at the class,
package, or project level, which can be specified here as “class,” “package,” or “project.”
The default is “project.”
metadataFileName
If the .jdo metadata has been stored at the “project” level, this is the file name of the .jdo
file.
detinationFile
This is the file name of the generated .java file. You must compile this file (probably by
using the javac Ant task) and include it on the classpath of your executing application.
classpath
This is the classpath from which classes to be enhanced (and their .jdo metadata) are
loaded.
You would use the GenerateMetaData task with something like this:
<genmetadata generatedPackageName="com.mvcsoft.generated"
generatedClassName="MyMetaData"
destinationFile="${src.dir}/com/mvcsoft/generated/MyMetaData.java
"
defaultTypesMap="${basedir}/${config.dir}/sapdb.xml"

169
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

persistenceClassList="${basedir}/${build.dir}/persistence_capable
.txt" >
<classpath path="${basedir}/${build.dir}" />
</genmetadata>
You would then compile the generated metadata with something like this:
<javac srcdir="${src.dir};${build.dir}"
destdir="${build.dir}"
includes="**/MyMetaData.java"
debug="on">
<classpath refid="project.class.path" />
</javac>
SchemaDDL
The class name of the Ant task to create or remove the required tables is
com.mvcsoft.jdo.ant_tasks.SchemaDDL. You would set up an Ant taskdef with
something like this:
<taskdef
name="schema"
classname="com.mvcsoft.jdo.ant_tasks.SchemaDDL"
classpathref="project.class.path"
/>
To use this task, the metadata class must be available as a compiled .class file on the
classpath. The JDBC driver must be available on the Ant classpath as well. The options
for the task are as follows:
generatedPackageName
This is the package name of the generated metadata class.
generatedClassName
This is the simple class name of the generated metadata class. (The fully-qualified name
is the generatedPackageName + “.” + generatedClassName.)
classpath
This is the classpath from which classes to be enhanced (and their .jdo metadata) are
loaded.
action
This mandatory parameter is the action that the Ant task should accomplish, either to
create or remove the tables. The two valid values are “create” and “remove.”
driverName
This mandatory parameter is the name of the JDBC driver with which to create or remove
the database tables.
connectionString

170
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

This mandatory parameter is the name of the JDBC connection string with which to
connect to the database.
userName
This parameter is the user name for the JDBC driver.
password
This parameter is the password for the JDBC driver.
removeKeyGenTables
This parameter indicates whether tables that hold next-valid key values should be
removed. If multiple applications share these tables, removing them will lead to duplicate
key errors. (The default is false.)
factoryproperties
This parameter provides the location of a properties file that provides additional values to
the PersistenceManagerFactory that the SchemaDDL task creates internally.
You would use the SchemaDDL task as follows. To create the schema:
<schema generatedPackageName="com.mvcsoft.generated"
generatedClassName="MyMetaData"
action="create"
driverName="com.sap.dbtech.jdbc.DriverSapDB"
connectionString="jdbc:sapdb://localhost/TST"
userName="TEST" password="TEST">
<classpath path="${basedir}/${build.dir}" />
</schema>
To remove the schema:
<schema generatedPackageName="com.mvcsoft.generated"
generatedClassName="MyMetaData"
action="remove"
driverName="com.sap.dbtech.jdbc.DriverSapDB"
connectionString="jdbc:sapdb://localhost/TST"
userName="TEST" password="TEST">
<classpath path="${basedir}/${build.dir}" />
</schema>

XDoclet
The XDoclet standard task jdodoclet is used to execute various jdo-specific sub-tasks.
MVCSoft provides three subtasks: one that generates a list of persistence-capable classes
(to submit to the enhancer and metadata generator), one that generates a list of
persistence-aware classes (to submit to the enhancer), and one that generates the .jdo
files.

You can do almost anything using XDoclet that you can do using the
MVCSoft GUI. There are three exceptions: using mapping transformers,

171
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

defining fault groups, and defining key factories. These produce


hierarchical data that is not well-suited for definition in XDoclet tags or
have pluggable implementations with dynamic configuration. The
recommended method for including these in your XDoclet builds is to
produce the configuration snippet using the GUI, and then including it in
your .jdo file through the extension mechanism of XDoclet or through
generic vendor extension tags.

Generating a List of Persistence Capable Classes


This subtask adds an entry into a text list for each of your classes that has a standard
@jdo.persistence-capable tag. (This tag is defined by the XDoclet-provided JDO
subtask.) Here is an example of its usage (with the relevant subtask in bold):
<jdodoclet
destdir="${build.dir}"
excludedtags="@version,@author,@todo">
<fileset dir="${src.dir}">
<include name="**/*.java"/>
</fileset>
<jdometadata generation="package"/>
<listpersistencecapable
destinationfile="persistence_capable.txt" />
<listpersistenceaware destinationfile="persistence_aware.txt"
/>

<mvcsoftjdo/>
</jdodoclet>
This creates the persistence_capable.txt file at the top-level of the destdir directory.
Generating a List of Persistence Aware Classes
This subtask adds an entry into a text list for each of your classes that has a
@jdo.persistence-aware tag. Here is an example of its usage (with the relevant subtask in
bold):
<jdodoclet
destdir="${build.dir}"
excludedtags="@version,@author,@todo">
<fileset dir="${src.dir}">
<include name="**/*.java"/>
</fileset>
<jdometadata generation="package"/>
<listpersistencecapable
destinationfile="persistence_capable.txt" />
<listpersistenceaware destinationfile="persistence_aware.txt"
/>

<mvcsoftjdo/>
</jdodoclet>
This creates the persistence_aware.txt file at the top-level of the destdir directory.

172
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Generating the .jdo Files


This is primary task for which the standard JDO XDoclet task was created. It is possible
to fully-configure .jdo metadata files for use with the MVCSoft metadata code generator
using only the standard JDO task. The MVCSoft vendor extensions could be added using
the @jdo.extension tag. However, MVCSoft also supplies a subtask that allows classes to
be decorated with tags specific to MVCSoft JDO data. Here is an example of using the
JDO XDoclet task with the MVCSoft subtask (with the relevant lines in bold):
<jdodoclet
destdir="${build.dir}"
excludedtags="@version,@author,@todo">
<fileset dir="${src.dir}">
<include name="**/*.java"/>
</fileset>
<jdometadata generation="package"/>
<listpersistencecapable
destinationfile="persistence_capable.txt" />
<listpersistenceaware destinationfile="persistence_aware.txt"
/>

<mvcsoftjdo/>
</jdodoclet>

MVCSoft JDO Tag Reference—Common Allowable Values in Tags


In both class and field level tags, there are places to specify jdbc types and prepared
statement method types. For jdbc types, the allowable values are taken from the standard
Java class java.sql.Types: BIT,TINYINT,SMALLINT,INTEGER,BIGINT,
FLOAT,REAL,DOUBLE,NUMERIC,DECIMAL,CHAR,VARCHAR,LONGVARCHA
R, DATE, TIME, TIMESTAMP, BINARY, VARBINARY, LONGVARBINARY,
OTHER, JAVA_OBJECT, DISTINCT, STRUCT, ARRAY, BLOB, CLOB, REF.
For prepared statement and result set method types, the allowable values are derived from
the classes java.sql.PreparedStatement and java.sql.ResultSet: BOOLEAN, BYTE,
SHORT, INT, LONG, FLOAT, DOUBLE, BIGDECIMAL, STRING, BYTES, DATE,
TIME, TIMESTAMP, ASCIISTREAM, UNICODESTREAM, BINARYSTREAM,
OBJECT, CHARACTERSTREAM, REF, BLOB, CLOB, and ARRAY.
MVCSoft JDO Tag Reference—Class Level Tags
Additional information for many of these options can be found elsewhere in this manual.
mvcsoft.discriminator
This tag is used with the classes of an inheritance hierarchy. The valid options are:
♦ value—the string indicator that will be associated with a concrete instance of this
class type

173
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ column-name—this option, used only with the base class, specifies the column name
in which the discriminator for the class hierarchy is stored
♦ sql-type—this option, used only with the base class, specifies the SQL type of the
column in which the discriminator for the class hierarchy is stored. It must be a type
in which a string can be stored
mvcsoft.locking
This tag is used to define locking strategies for a class. (The default is no application-
layer locking, i.e. to defer locking completely to the database.) The valid options are:
♦ strategy—the overall mechanism by which locking occurs. Valid values are counter,
timestamp, or compare-fields
♦ column-name—for counter or timestamp strategies only, the database column name
of the counter or timestamp
♦ data-transformer—for counter or timestamp strategies only, the name of the data
transformer class for the counter or timestamp
♦ jdbc-type—for counter or timestamp strategies only, the JDBC type for the counter or
timestamp (see the section on common allowable values in tags)
♦ prepared-statement-method—for counter or timestamp strategies only, the prepared
statement method name for the counter or timestamp (see the section on common
allowable values in tags)
♦ result-set-method—for counter or timestamp strategies only, the result set method
name for the counter or timestamp (see the section on common allowable values in
tags)
♦ sql-type—the SQL type of the column, used for DDL
mvcsoft.datastore-key
These configuration options can be used with classes for which the runtime provides the
key.
♦ generator-name—the generator name for the key value
♦ java-type—the format of the datastore key value stored internally by the class; one of
DEFAULT, STRING, BIGINT, LONG, and INT. The STRING value results in a
type of java.lang.String; BIGINT results in a type of java.math.BigInteger; LONG
results in a java type of java.lang.Long; and INT results in a type of java.lang.Integer.
You should ensure that other values (such as jdbc-type and sql-type) are consistent
with this value. The DEFAULT value defers the decision to the key generator. If you
choose DEFAULT, you should either (1) be familiar with the default java-type of the
specified key generator, or (2) leave all other values to their default as well.
♦ column-name—the column name of the datastore key

174
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ data-transformer—the name of the data transformer class for the datastore key value
♦ jdbc-type—the JDBC type of the datastore key column(see the section on common
allowable values in tags)
♦ prepared-statement-method—the prepared statement method name (see the section on
common allowable values in tags)
♦ result-set-method—the result set method name (see the section on common allowable
values in tags)
♦ sql-type—the SQL type of the column, used for DDL
sql.table
♦ table-name—the name of the database table in which the object’s basic state is stored
MVCSoft JDO Tag Reference—Field Level Tags
Additional information for many of these options can be found elsewhere in this manual.
mvcsoft.singleton-element
♦ type—the fully-qualified class name of the persistence-capable class stored in this
field, for relationships only
mvcsoft.lock-comparison
♦ exclude—whether or not this field should be excluded from the compare-fields
locking strategy comparison; true or false (default is false)
mvcsoft.map-as
♦ policy—whether the field is mapped as a column in the table, or as a relationship;
valid values are relationship or column
mvcsoft.persistence-by-reachability
♦ check—whether or not the runtime engine should navigate this field when running the
persistence-by-reachability algorithm
sql.field
♦ second-class-object—the MVCSoft-supplied class that provides second-class-object
semantics as per the JDO specification; valid values are LINKED_LIST,
HASH_SET, ARRAY_LIST, TREE_SET, VECTOR,SERIALIZED_LINKED_LIST,
SERIALIZED_ARRAY_LIST, SERIALIZED_VECTOR,
SERIALIZED_HASH_MAP,SERIALIZED_TREE_MAP,
SERIALIZED_HASH_TABLE, SERIALIZED_HASH_SET,
SERIALIZED_TREE_SET, SERIALIZED_ARRAY, JAVA_UTIL_DATE,
JAVA_SQL_DATE, JAVA_SQL_TIME, JAVA_SQL_TIMESTAMP, HASH_MAP,
HASH_TABLE, TREE_MAP, and ARRAY

175
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ column-name—the name of the database column


♦ jdbc-type—the JDBC type of the column (see the section on common allowable
values in tags)
♦ prepared-statement-method—the prepared statement method name (see the section on
common allowable values in tags)
♦ result-set-method—the result set method name (see the section on common allowable
values in tags)
♦ sql-type—the SQL type of the column, used for DDL
♦ data-transformer—the fully-qualified class name of the data transformer, if any
♦ constant-transformer—the fully-qualified class name of the constant transformer, if
any
♦ comparator—the fully-qualified class name of a class that implements
java.util.Comparator, for use in implementations of TreeSet and TreeMap assigned to
this field
♦ fault-group—the name of the fault group that will be used when this field is loaded at
runtime
♦ sqlindex-name—the name of any index on this field
♦ sqlindex-type—the type of index: none (the default), indexed, and unique
sql.relation
♦ style—relationships can be mapped to a relationship table or by adding foreign keys
to the parent or child table, subject of course to constraints from relationship
cardinality. Valid values are relation-table or foreign-key
♦ foreign-key-location—a foreign-key-mapped relationship can add that key to the
parent or child table, subject of course to constraints from relationship cardinality.
Valid values are parent (the declaring class for the field) or child (the class type
managed by the field)
♦ relationship-table-name—the name of the relationship table in the database
♦ related-field—the name of the reciprocal field in the child class, if it should be
mapped to the same relational construct
♦ fault-group—the name of the predefined fault group to use by default when loading
this relationship at runtime. (For a java.util.Map-based mapping, this represents the
fault group associated with the “value” class.)
♦ map-key-fault-group—for a java.util.Map-based relationship only, this represents the
predefined fault group associated with the “key” class

176
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ relationship-key-aliases—for a field mapped to a relationship, this allows you to


specify the column names of the relationship table or foreign key columns. The
format is simple: {role}:{fieldname}={column name}[,{fieldname}={column
name}]*[;{role}:{fieldname}={column name}[,{fieldname}={column name}]*]
where {role} is one of parent, child, or key (where parent is the class declaring the
field, child is the class type as contents of the field, and key is for map key columns in
tables that represent java.util.Map relationships); {fieldname} is either one of the
field names composing an application-identity type key or the keyword datastore-
key; and {column-name} is the name to use in the database
♦ parent-fk-name—used only for a relationship mapped to a table; the name of the
foreign key between the parent object and the collection or map object
♦ key-fk-name—used only for a java.util.Map relationship; the name of the foreign key
between the relationship table and the key object
♦ value-fk-name—used for all relationship types; the name of the foreign key between
the relationship table and the value object, or for a foreign-key mapped relationship
Here are some examples of the usage of the relationship-key-aliases tag. The following
shows a relationship mapped to a foreign key in the child. This means that there will be a
column in the child class’s table for every key field in the parent. Here, we provide
aliases for the fields keyParentOne and keyParentTwo. As a result, the child table will
have two foreign-key columns named “key1” and “key2.”
* @sql.relation
* style="foreign-key"
* foreign-key-location="child"
* relationship-key-
aliases="parent:keyParentOne=key1,keyParentTwo=key2"
The following shows a similar relationship mapped to a relationship table:
* @sql.relation
* style="relation-table"
* relationship-table-name="appidreltable1"
* relationship-key-
aliases="parent:keyParentOne=pkey1,keyParentTwo=pkey2;child:keyChildOne
=ckey"
Here is a relationship mapping that uses datastore keys, rather than app identity keys:
* @sql.relation
* style="foreign-key"
* foreign-key-location="child"
* relationship-key-aliases="parent:datastore-key=myFk"
Finally, here is a relationship mapping for a java.util.Map based relationship:
* @sql.relation
* relationship-key-aliases="parent:datastore-
key=myPrntPk;key:datastore-key=myKeyPk;child:datastore-key=myValuPk"
* relationship-table-name="dskeychldothr"

177
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

mvcsoft.basic-type-value-mapping
If a relationship collection or array manages basic types (such as Strings or primitive
wrappers), this tag allows you to define the storage of that type in the database. If the
relationship collection is based on java.util.Map, this represents the value type.
♦ column-name—the name of the database column
♦ jdbc-type—the JDBC type of the column (see the section on common allowable
values in tags)
♦ prepared-statement-method—the prepared statement method name (see the section on
common allowable values in tags)
♦ sql-type—the SQL type of the column, used for DDL
♦ data-transformer—the fully-qualified class name of the data transformer, if any
♦ constant-transformer—the fully-qualified class name of the constant transformer, if
any
mvcsoft.basic-type-key-mapping
If a java.util.Map-based relationship manages basic types as its key (such as Strings or
primitive wrappers), this tag allows you to define the storage of that key type in the
database.
♦ column-name—the name of the database column
♦ jdbc-type—the JDBC type of the column (see the section on common allowable
values in tags)
♦ prepared-statement-method—the prepared statement method name (see the section on
common allowable values in tags)
♦ result-set-method—the result set method name (see the section on common allowable
values in tags)
♦ sql-type—the SQL type of the column, used for DDL
♦ data-transformer—the fully-qualified class name of the data transformer, if any
♦ constant-transformer—the fully-qualified class name of the constant transformer, if
any
mvcsoft.indexed-relationship
If ordering information should be stored with the relationship collection or array, a
numeric field is added to the database relationship table and used as an integer by the
runtime. The storage of this integer can be configured like any other managed field.
♦ indexed—true or false (default is true if mvcsoft.indexed-relationship tag present)
♦ column-name—the name of the database column

178
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ jdbc-type—the JDBC type of the column (see the section on common allowable
values in tags)
♦ prepared-statement-method—the prepared statement method name (see the section on
common allowable values in tags)
♦ result-set-method—the result set method name (see the section on common allowable
values in tags)
♦ sql-type—the SQL type of the index column, used for DDL
♦ sqlindex-name—the name of the column index on the index field
♦ sqlindex-type—the type of the column index on the index field; valid values are
“indexed” (the default) and “none”
Fault Groups and Key Factories
Fault groups and key factories are important parts of your application. However, their
nested structures (in the case of fault groups) or dynamic natures (in the case of key
factories, which will be pluggable in a future release) make it difficult to supply custom
tags. One approach is to configure appropriate values in the MVCSoft GUI and to copy-
and-paste the results into a standard @jdo.class-vendor-extension tag that might look
something like this:
/**
* @jdo.class-vendor-extension
* vendor-name="mvcsoft"
* key="key-factory"
* value="SAMPLE_BLOCKS_FACTORY"
* content="<extension vendor-name=\"mvcsoft\"
key=\"sequenceColType\" value=\"varchar(30)\"/>
<extension vendor-name=\"mvcsoft\" key=\"sequenceName\"
value=\"myTestSequence\"/>
<extension vendor-name=\"mvcsoft\" key=\"retryCount\" value=\"2\"/>
<extension vendor-name=\"mvcsoft\" key=\"incrementColName\"
value=\"myIncColName\"/>
<extension vendor-name=\"mvcsoft\" key=\"class\"
value=\"com.mvcsoft.jdo.runtime.util.KeyBlocks\"/>
<extension vendor-name=\"mvcsoft\" key=\"sequenceColName\"
value=\"mySeqColName\"/>
<extension vendor-name=\"mvcsoft\" key=\"incrementColType\"
value=\"integer\"/>
<extension vendor-name=\"mvcsoft\" key=\"tableName\"
value=\"myTestTable\"/>
<extension vendor-name=\"mvcsoft\" key=\"name\" value=\"
SAMPLE_BLOCKS_FACTORY \"/>
<extension vendor-name=\"mvcsoft\" key=\"chunkSize\" value=\"5\"/>
<extension vendor-name=\"mvcsoft\" key=\"associatedClass\"
value=\"development_tests.reciprocal.RecipParent\"/>"
*/

179
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

(Note that key factories are stored with a particular class, but may be used by any class,
not just the one with which they are stored.)

180
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

JDO Callbacks
There are two types of user callbacks defined by the JDO specification: transaction
callbacks and persistence life-cycle instance callbacks.
Transaction Callbacks
JDO programmers can be notified before and after transaction synchronization, by
registering a class that implements javax.transaction.Synchronization with the relevant
javax.jdo.Transaction instance. These notifications might be used to perform additional
validation on the objects prior to commit, do cleanup post-commit, etc. There can be only
one javax.transaction.Synchronization instance registered with a particular transaction; if
multiple callbacks are required, the JDO programmer is required to chain the
notifications him- or herself.
Sample Using Transaction Callbacks
This example demonstrates the basics of using transaction callbacks. The following is a
simple persistence-capable class used with this example:
package com.mvcsoft.jdosamples.transactions.callbacks;

public class ObjToTest


{
private String value1;
private String value2;

public ObjToTest()
{
}

public ObjToTest(String value1, String value2)


{
this.value1 = value1;
this.value2 = value2;
}

public String getValue1()


{
return value1;
}

public void setValue1(String value1)


{
this.value1 = value1;
}

public String getValue2()


{
return value2;
}

181
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public void setValue2(String value2)


{
this.value2 = value2;
}
}
Here we register a transaction synchronization object and use it for pre-commit
validation. In the post-commit callback, we print out the status of the transaction.
package com.mvcsoft.jdosamples.transactions.callbacks;

import javax.jdo.*;
import javax.transaction.Synchronization;
import javax.transaction.Status;
import java.util.Properties;

public class TestTransactionCallback


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.transactions.callbacks.MyGeneratedData" );

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
pm.currentTransaction().begin();

182
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

final ObjToTest object = new ObjToTest("one", "two");


pm.makePersistent(object);

Here’s where we set our synchronization object (an inner class).


pm.currentTransaction().setSynchronization(new Synchronization()
{
public void beforeCompletion()
{
If you comment out this test in beforeCompletion, the transaction will complete
successfully.
if (object.getValue1().equals("one"))
throw new JDOUserException("Validation failed");
}

Here we just use afterCompletion to print out the status of the transaction.
public void afterCompletion(int status)
{
if (status == Status.STATUS_COMMITTED)
System.out.println("committed");
else if (status == Status.STATUS_ROLLEDBACK)
System.out.println("rolled back");
else
System.out.println("Unknown status");
}
});

// commit--but really we'll see a rollback


pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}

183
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Instance Callbacks
Four callbacks are available to your persistence-capable objects for processing: after the
persistent state has been loaded (jdoPostLoad), before it is saved (jdoPreStore), before it
is deleted (jdoPreDelete), and before the Java instance state is cleared by the runtime
(jdoPreClear). These callbacks are often used to implement validation, cascade delete,
and calculated fields. Other uses are of course possible.
To take advantage of these callbacks for a particular persistent object, implement the
interface javax.jdo.InstanceCallbacks.
jdoPostLoad
The jdoPostLoad method can be used to calculate values of non-persistent fields after the
persistent data has been loaded into the object. The following sample will demonstrate
this functionality, where a non-persistent displayName field is calculated using a fName
and a lName persistent field.
The jdoPostLoad method has some stringent limitations: you cannot access other
persistence-capable objects in this method, and fields that weren’t already loaded will not
be “faulted in” if accessed in this method—so make sure that the fault groups you use
with that class are compatible with the requirements of jdoPostLoad.

The JDO specification requires that the jdoPostLoad method is called after
the fields in the default fetch group are loaded. The “default fetch group”
is the spec’s limited concept of fault groups—there being only a single
fault group, consisting of the non-key fields of primitive types or
wrappers, and fields of type java.util.Date. Note that there is no way in the
spec to specify a particular fault group to use, so under most
circumstances jdoPostLoad is called whenever the state of an object is first
loaded. The exception is when the first field requested from an object--
whose identity has been established but has no state currently loaded—is
not a member of the default fetch group. MVCSoft calls jdoPostLoad
regardless of the fault group used. Be aware that the jdoPostLoad method
is not enhanced, and state will not be dynamically faulted in if it has not
already been loaded, so you should design your fault groups always to
provide whatever state is required in your jdoPostLoad method.

jdoPreStore
The jdoPreStore method is called before the object state is saved to the database. In one
respect, jdoPreStore is the matching method to jdoPostLoad, in that values of modified
non-persistent fields can be applied to persistent fields. The following sample will
demonstrate this functionality, where the non-persistent displayName field will be parsed
(if modified) and saved in the fName and lName fields.

184
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

However, jdoPreStore has another function: validation. The transaction can be rolled
back by throwing an exception from this method, if the object state has errors. We’ll see
an example of this as well.
Furthermore, jdoPreStore does not have the same limitations as jdoPostLoad: other
persistent objects can be accessed, and persistent state can be dynamically loaded.
jdoPreDelete
The jdoPreDelete method is called before the object state is removed from the database.
It is typically used for two purposes: validation; and implementing cascade delete. We’ll
see an example of the cascade-delete functionality in the following sample.
jdoPreClear
The jdoPreClear method is called when the runtime clears the object state—depending on
various settings—after a transaction commits or rolls back. It is used to give the JDO
developer an opportuntity to clear the object fields that are not managed by the JDO
runtime. In this method, the JDO runtime does not track access to persistent fields or
allow them to be dynamically loaded (similar to jdoPostLoad). We’ll see an example of
how to use jdoPreClear in the following sample.
Sample PersistenceCapable Class With Callbacks
The following class makes use of all four callbacks. It has a non-persistent field whose
value is set during jdoPostLoad from persistent data, and whose modification is tracked
and saved in jdoPreStore. The class’s relationship is cascade-deleted in jdoPreDelete, and
the non-persistent state is cleared in jdoPreClear. Here is the class:
package com.mvcsoft.jdosamples.callbacks;

import javax.jdo.InstanceCallbacks;
import javax.jdo.JDOUserException;
import javax.jdo.JDOHelper;

public class CallbackP implements InstanceCallbacks


{
private String fName;
private String lName;
Remember that transient fields, by default, are not managed by the JDO runtime. (Of
course, we could change this in the .jdo metadata, just like we could manage transient
fields by changing the .jdo metadata.)
private transient String displayName;
private transient boolean displayNameSet;
private CallbackC child;

public CallbackP()
{
}

public CallbackP(String displayName, CallbackC child)

185
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
setDisplayName(displayName);
this.child = child;
}

public String getDisplayName()


{
return displayName;
}
We track whether or not changes have been made to the displayName with the
displayNameSet boolean variable. Otherwise, we would always need to resave the object
state. (In this case, we could also compare the old values of fName and lName with the
new values to see if a modification were necessary.)

public void setDisplayName(String displayName)


{
this.displayName = displayName;
this.displayNameSet = true;
}

public CallbackC getChild()


{
return child;
}

public void setChild(CallbackC child)


{
this.child = child;
}

// callback methods
Here we initialize displayName from the persistent state.

public void jdoPostLoad()


{
displayName = lName + "," + fName;
}
Here we save any changes made to the non-persistent displayName by breaking it down
and storing it in persistent fields. Note we also do validation, by ensuring that the display
name has a comma in it. If the JDOUserException is thrown, the transaction will roll
back.

public void jdoPreStore()


{
if (!displayNameSet)
return;
if (displayName == null)
{
fName = null;

186
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

lName = null;
}
else
{
int i = displayName.indexOf(",");
if (i == -1)
throw new JDOUserException("Illegal displayname format set");
lName = displayName.substring(0, i);
fName = displayName.substring(i+1);
}
}
The jdoPreDelete method cascades the delete to the child record. If this method didn’t
exist, the relationship would be disolved but the child record would still exist.

public void jdoPreDelete()


{
if (child != null)
JDOHelper.getPersistenceManager(this).deletePersistent(child);
}
We clear the transient fields here. If we didn’t, they would be set incorrectly when the
persistent state was next loaded. (The displayNameSet would still be true—and the state
would inadvertantly be resaved.)

public void jdoPreClear()


{
displayName = null;
displayNameSet = false;
}

}
Child Class Used in Sample
This is the class referenced in the relationship.
package com.mvcsoft.jdosamples.callbacks;

public class CallbackC


{
private String testValue;

public CallbackC()
{
}

public CallbackC(String testValue)


{
this.testValue = testValue;
}

public String getTestValue()


{

187
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

return testValue;
}

public void setTestValue(String testValue)


{
this.testValue = testValue;
}
}
Sample Program
Here is a simple sample program that demonstrates all four callbacks in action.
package com.mvcsoft.jdosamples.callbacks;

import javax.jdo.*;
import java.util.Properties;

public class TestCallbacksMain


{
private static Properties getStandardProperties()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.callbacks.MyMetaData" );

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

return properties;
}

private static PersistenceManager getStandardPersistenceManager()


{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());

return factory.getPersistenceManager();
}

public static void main(String[] args)


{
PersistenceManager pm = null;
try
{
pm = getStandardPersistenceManager();
// create

188
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

System.out.println("CREATING");
pm.currentTransaction().begin();
CallbackC child = new CallbackC("testC");
CallbackP parent = new CallbackP("OConnor,Daniel", child);
pm.makePersistent(parent);
Object oid = JDOHelper.getObjectId(parent);
When the transaction is committed, the jdoPreStore callback will be called, and the
fName and lName fields will be stored.
pm.currentTransaction().commit();

// read
System.out.println("READING");
pm.currentTransaction().begin();
When we retrieve the values, jdoPostLoad will be called and the displayName variable
will be initialized. (If we just accessed the displayName variable, the loading of the
persistent state wouldn’t be triggered because it isn’t a managed field. The retrieve
method loads the object state.)
pm.retrieve(parent);
System.out.println(parent.getDisplayName());
pm.currentTransaction().commit();

// deleting
pm.currentTransaction().begin();
System.out.println("DELETING");
pm.deletePersistent(parent);
When the transaction commits, jdoPreDelete will be called and the child record will be
deleted as well.
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
try
{
if (pm.currentTransaction().isActive())
pm.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
}

189
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Schema Update Tool


During development of an application, when the object model (and therefore the database
schema) changes, it is often simplest to simply delete the old schema and and recreate it.
However, this is no longer an option once you have a production database with data you
need to preserve. The MVCSoft JDO 2.0 release offers a schema update tool to assist in
migrating data from one release of your application to the next. This tool can:
♦ add and remove columns to respond to class changes
♦ add an optimistic locking increment column, and initialize existing records with a 0
♦ add a discriminator column if a class now is part of an inheritance hierarchy, and
initialize existing records with the discriminator value for the base class
♦ rename fields or change types
♦ change any variation of a relationship mapping: from table-based to a foreign key in
the parent or the child; a foreign key in the parent or child to table-based, a foreign
key in the parent to a foreign key in the child, or a foreign key in the child to a foreign
key in the parent (and move the existing data)
It can't do the following:
♦ change a collection class from a serialized type to a relationship (because there is no
mechanism to convert the existing data)
♦ change the number of columns in a primary keys for objects
♦ change mapping transformations (ie change the representation of a field from one
column to two or more columns, or vice versa)
The scope of the schema update tool is greater than that of an alter table statement, and
the schema update tool is not intended to make these changes on a database with
concurrent transactions.

The schema update tool allows you to generate the sql (and rework it or
distribute it with your product), or update the database directly. With
either technique, MVCSoft strongly recommends (on general principles)
that all changes to a production database be carefully reviewed, and that
the database is backed up before any changes are applied.

How to Invoke the Schema Update Tool

190
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

You can invoke the schema update tool from the GUI tool’s top-level Database menu
item, using the Update Schema… option (to make changes directly to the database) or
Display Schema Changes (to generate the DDL necessary to update the database, for
review or execution using a different tool).
The schema tool works by comparing the metadata class from an earlier version to the
metadata class from the current version, and generating the required changes. You must
specify the classpath and the metadata class name for the prior version of your
application. Other dialog options are the same as the Create Tables… or Display
Tables… dialog.
Configuring the Behavior
The SQL generated by the schema update tool is determined by templates associated with
particular tasks. The following is a list of tasks that the schema update tool performs:
♦ rename table
♦ add column
♦ rename column
♦ drop column
♦ change column type
♦ change column name and type
♦ add lock field
♦ add discriminator field
♦ drop primary key
♦ add primary key
♦ move relationship to foreign key
♦ move relationship to table
Each one of these actions has a template, which describes one or more SQL statements to
be executed. You can change the templates by using PersistenceManagerFactory
properties (the defaults work with an Oracle database).
The templates use either simple parameter substitution, or for more complicated
requirements a simple foreach syntax. The meaning of the parameters is specific to each
action, and is documented below. Multiple statements can be generated from one
template by using a semi-colon separator.
The foreach syntax uses a ? parameter to determine over what to iterate, and a $
parameter for the values in each iteration. A common need is to have something after all
but the last value (e.g. a comma or an “and”); this is specified in brackets {} at the end of
the statement. The defaults below provide examples that make this clear.

191
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Here are the PersistenceManagerFactory properties, the meanings of the parameters, and
the default values (set up to work with Oracle):
com.mvcsoft.jdo.sql.AlterTableRenameTemplate
♦ ?1 old table name
♦ ?2 new table name
"alter table ?1 rename to ?2"

com.mvcsoft.jdo.sql.AlterTableAddColumnTemplate
♦ ?1 table name
♦ ?2 new column name
♦ ?3 new column type
"alter table ?1 add ?2 ?3"

com.mvcsoft.jdo.sql.AlterTableRenameColumnTemplate
♦ ?1 table name
♦ ?2 old column name
♦ ?3 new column name
"alter table ?1 add ?3 ?5; update ?1 set ?3 = ?2; alter table ?1 drop
column ?2"

com.mvcsoft.jdo.sql.AlterTableDropColumnTemplate
♦ ?1 table name
♦ ?2 column name
"alter table ?1 drop column ?2"

com.mvcsoft.jdo.sql.AlterTableChangeColumnTypeTemplate
♦ ?1 table name
♦ ?2 old column name
♦ ?3 new column name

192
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ ?4 old column type


♦ ?5 new column type
"alter table ?1 modify ?2 ?5"

com.mvcsoft.jdo.sql.AlterTableChangeColumnNameAndTypeTemplate
♦ ?1 table name
♦ ?2 old column name
♦ ?3 new column name
♦ ?4 old column type
♦ ?5 new column type
"alter table ?1 add ?3 ?5; update ?1 set ?3 = ?2;
alter table ?1 drop column ?2"

com.mvcsoft.jdo.sql.AlterTableAddLockFieldTemplate
♦ ?1 table name
♦ ?2 new column name
♦ ?3 new column type
"alter table ?1 add ?2 ?3; update ?1 set ?2=0"

com.mvcsoft.jdo.sql.AlterTableAddDiscriminatorFieldTemplate
♦ ?1 table name
♦ ?2 new column name
♦ ?3 new column type
♦ ?4 base class discriminator value
"alter table ?1 add ?2 ?3; update ?1 set ?2='?4'"

com.mvcsoft.jdo.sql.AlterTableDropPrimaryKey
♦ ?1 table name
♦ ?2 column names
"alter table ?1 drop primary key"

193
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

com.mvcsoft.jdo.sql.AlterTableAddPrimaryKey
♦ ?1 table name
♦ ?2 column names
"alter table ?1 add primary key ([foreach ?2 $2{,}])"

com.mvcsoft.jdo.sql.MoveRelationshipToFk
♦ ?1 source table name
♦ ?2 destination table name
♦ ?3 key fields source table
♦ ?4 key fields destination table
♦ ?5 fk fields source table
♦ ?6 fk fields destination table
"update ?2 [foreach ?6 set $6 = (select $3 from ?1 where [foreach ?4
?1.$5=?2.$4{ and }] ),}]"

com.mvcsoft.jdo.sql.MoveRelationshipToTable
♦ ?1 source table name
♦ ?2 destination table name
♦ ?3 key fields source table
♦ ?4 key fields destination table
♦ ?5 fk fields source table
♦ ?6 fk fields destination table
"insert into ?2 ([foreach ?4 $4{,}],[foreach ?6 $6{,}]) select [foreach ?3
$3{,}],[foreach ?5 $5{,}] from ?1"

194
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Reflection and JDO


One of the strengths of JDO is its ability to manage the state of your Java objects
automatically. It does this by tracking when fields of managed objects are read and
written, and performing the necessary database i/o at appropriate points based on this
information.
There are a variety of possible strategies for the tracking of these fields. For instance,
EJB CMP 2.0 tracks changes by hiding persistent fields behind abstract methods, and
mandating that all changes go through these methods. (It is then a simple matter for the
EJB container to provide a concrete implementation of these methods that also does the
required bookkeeping.)
The strategy that JDO 1.0 selected is enhancement. Each access of a persistent field in the
user’s application is replaced with an access of a special method. This method gives the
runtime framework the opportunity to do the required state management. All this happens
without any special coding requirements.
However, the use of reflection to access these fields directly bypasses the JDO runtime
and its state management. This is what the JDO 1.0 specification has to say about
reflection:

20.8 Introspection (Java core reflection)


No changes are made to the behavior of introspection. The current state of
all fields is exposed to the reflection APIs. This is not at all what some
users might expect. It is a future objective to more gracefully support
introspection of fields in persistent instances of persistence capable
classes.

One good alternative to introspecting fields directly is instead to use reflection with
accessor and mutator methods (get and set methods). These methods can be used with
reflection exactly as they would be used in Java code, with no difference in behavior by
the JDO runtime.
If reflection using fields is desired despite the disadvantages, it is still possible. To read
field values, the user should ensure that the values have been loaded. One possibility is
to use the retrieve method or one of the retrieveAll methods of the PersistenceManager
interface.
To write field values, the user should notify the runtime that the field value has been
modified. There is a standard API method in the JDOHelper class to accomplish this:
static void makeDirty(Object pc, String fieldName). (This method is also used when an
array referenced by a persistent class is changed.)
Unfortunately, by the time the runtime receives this programmatic notification the value
has already been changed. If you want the value backed up for use in field-comparison-

195
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

based optimistic transaction management or for value restoration, you must use a
proprietary MVCSoft method. Before you change the field value using reflection, cast
the PersistenceManager instance associated with the object to the
MVCSoftPersistenceManager interface. Call the method makeBackupOfField(Object pc,
String fieldName). If you do not use this method, the transaction will still commit
correctly but the field will not participate in any optimistic verification.

196
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Blobs and Clobs


The MVCSoft toolkit provides a great deal of flexibility in how your data is treated, using
a generalized system of data transformers and configuration properties. For the most part,
the reasonable default values work fine, and fine-tuning them is almost self-explanatory.
This chapter explains the small amount of configuration you need to do to map your
fields to a column of type blob or clob.
Fundamentally, the configuration requirements come from the JDBC specification. It
allows stream methods to be used to set values for CLOBS and BLOBs, and the only
portable alternative is to retrieve them from a prior SQL statement. See for example
JDBC 3.0 FR 16.3.2. The ResultSet value, on the other hand, cannot be retrieved using
streams so we use the clob or blob interface directly.
To achieve this result, we use a data transformer to produce the stream for the state-to-
database transformation, and to read the value from the blob or clob to produce the String
or byte[]. There are three pre-written data transformers that can be used (and you can
supply your own as well): String as CLOB; byte[] as BLOB; and Simple Serializable as
BLOB.
Use the following properties for a CLOB field:
Jdbc Type: CLOB
PreparedStatement Method: CHARACTERSTREAM
ResultSet Method: CLOB
SQL Type: clob
Data Transformer: String as CLOB
Use the following properties for a BLOB field:
Jdbc Type: BLOB
PreparedStatement Method: BINARYSTREAM
ResultSet Method: BLOB
SQL Type: blob
Data Transformer: byte[] as BLOB or SimpleSerializable as BLOB

197
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Managing Large Result Sets Effectively


Small sets of objects can be reasonably and efficiently managed in an application’s
working memory during a transaction or during a non-transactional read. However, JDO
queries and extents can potentially represent millions of records in their result set. A
naïve approach of loading the entire working set into memory would not be an effective
use of resources in these cases. MVCSoft provides for four distinct approaches to
handling result sets, behind (for segmented lists) a simple java.util.List API. The
approaches are:
1) Load the entire set into memory (appropriate for reasonably-sized result sets); this
is the default
2) Load a user-requested segment of the result set at a time, using a database-specific
“limit” or “limit/offset” keyword—ideal for databases that support this
functionality such as Postgresql or mySQL
3) Load a user-requested segment of the result set at a time, using a scrollable cursor
(probably implemented in your JDBC driver by stepping through and caching the
results). Make sure you close your query or extent in this case, because
otherwise the result set will stay open!
4) Load a user-requested segment of the result set at a time, using a simple repeated
iteration through the result set to get to the starting point

Retrieving a subset of the results is incompatible with fault groups that


navigate through a multi-valued relationship. Fault groups used with
subset strategies will be rewritten by the runtime to conform to this
limitation.

Setting the Appropriate Strategy


The user sets the appropriate strategy in the javax.jdo.Extent class, using an MVCSoft-
specific API by casting the Extent class to an MVCSoft interface:
package com.mvcsoft.jdo.runtime.user;

public interface MVCSoftExtent


{
public static final int ALL_RESULTS = 0;
public static final int SUBSET_LIMIT = 1;
public static final int SUBSET_LIMITOFFSET = 2;
public static final int SUBSET_JDBC_CURSOR = 3;
public static final int SUBSET_ITERATE_RESULTS = 4;

public void setSubsetTechnique(int technique, int chunkSize);


public void setSubsetTechniqueCursor(int chunkSize,
int resultSetType);
}

198
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

The technique parameter should be one of the constants defined in the MVCSoftExtent
class.
The chunkSize parameter refers to the number of records that are retrieved at one time.
This number should be set to a reasonable value, depending on the needs of the
application and the limitations of the execution environment. If the transaction will be
dealing with a fixed number of records (e.g. for a page of user interface data), that should
probably be the chunk size. If this number is set too low, queries to the database will be
increased unnecessarily.
The resultSetType parameter refers to the desired java.sql.ResultSet.TYPE_XXX value.
Valid values are TYPE_FORWARD_ONLY (which may conflict with the runtime’s use
of absolute(), depending on your JDBC driver), TYPE_SCROLL_INSENSITIVE, and
TYPE_SCROLL_SENSITIVE.
Accessing the Results
There are two circumstances in which result sets might be best accessed as segmented
lists: iterating extents; and navigating JDO query results.
Iterating Extents
By design, there are no collection methods in the javax.jdo.Extent class other than the
iterator method, which allows only for forward iteration. You cannot determine the size
of the extent, skip ahead to a particular index in the extent, or determine if a particular
object is a member.
The process of breaking down the extent into subsets is done automatically during
iteration, with a subset of size chunk being retrieved automatically when necessary.
In general, iteration with an Extent is of marginal use. An Extent should be configured
and then used with a JDO Query (even if there is no where clause in the query to limit
membership in the extent).
Navigating JDO Query Results
A query returns a java.lang.Object instance from its execute() method. The JDO
specification requires that this be a read-only instance of java.util.Collection. The
MVCSoft JDO Toolkit allows you to cast this to a java.util.List interface (the
implementation of which will also be read-only).
The instance implementing java.util.List will automatically manage list subsets and
database queries based on the configuration of the javax.jdo.Extent instance used to
initialize the javax.jdo.Query, and based on the parameters passed to its List API.
The java.util.List.size() method will result in a query to the database of the form select
count (*), rather than selecting any particular data or counting the elements in the list.
This is useful in a variety of circumstances, such as controlling the page size for
navigation controls in a GUI client. The java.util.List.isEmpty() method simply uses
with same functionality by returning size()==0.

199
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Methods that operate on a particular index, such as the java.util.List.get(int index)


method or the java.util.List.subList(int fromIndex, int toIndex) method will result in a
new subset being loaded, if necessary. (Methods, such as subList, that deal with a range
of records may result in the size of the subset being increased to avoid multiple database
accesses to satisfy their requirements.)
List operations that by their nature operate on the entire list are not supported. Examples
are public boolean contains(Object o), or public Object[] toArray().
Sample Code
The following two examples will demonstrate using the subset mechanism: one with an
extent (using a scrollable cursor), and one with a query (using simple iteration). They
make use of the following persistence capable object:
package com.mvcsoft.jdosamples.queries.subsets;

public class RabbitObject


{
private String someVal1;
private String someVal2;
private String someVal3;

public RabbitObject()
{
}

public RabbitObject(String someVal1, String someVal2, String


someVal3)
{
this.someVal1 = someVal1;
this.someVal2 = someVal2;
this.someVal3 = someVal3;
}

public String getSomeVal1()


{
return someVal1;
}

public void setSomeVal1(String someVal1)


{
this.someVal1 = someVal1;
}

public String getSomeVal2()


{
return someVal2;
}

public void setSomeVal2(String someVal2)


{
this.someVal2 = someVal2;

200
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public String getSomeVal3()


{
return someVal3;
}

public void setSomeVal3(String someVal3)


{
this.someVal3 = someVal3;
}

}
The following Java class creates 100 persistent instances of the persistence capable
object:
package com.mvcsoft.jdosamples.queries.subsets;

import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import java.util.Properties;
import java.util.ArrayList;

public class MakeManyMain


{
public static void main(String[] args)
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.queries.subsets.MyMetaData");

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(properties);
PersistenceManager persistenceManager =
factory.getPersistenceManager();
try
{
ArrayList arrayList = new ArrayList();
for (int i=0; i<100; i++)
{

201
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

arrayList.add(new RabbitObject(Integer.toString(i),
Integer.toString(i),Integer.toString(i)));
}
Transaction transaction =
persistenceManager.currentTransaction();
transaction.begin();
persistenceManager.makePersistentAll(arrayList);
transaction.commit();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
if (persistenceManager.currentTransaction().isActive())
persistenceManager.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
persistenceManager.close();
}

}
Iterating an Extent
The following class demonstrates how to manage a large result set while iterating an
Extent, using a scrollable cursor.
package com.mvcsoft.jdosamples.queries.subsets;

import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;
import java.sql.ResultSet;

import com.mvcsoft.jdo.runtime.user.MVCSoftExtent;

public class ExtentSubsetCursorMain


{
public static void main(String[] args)
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.queries.subsets.MyMetaData");

202
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(properties);
PersistenceManager persistenceManager =
factory.getPersistenceManager();
try
{
Transaction transaction =
persistenceManager.currentTransaction();
transaction.setNontransactionalRead(true);
Extent extent = persistenceManager.getExtent(RabbitObject.class,
true);
Here we configure the extent to use a scrollable cursor, using the MVCSoftExtent
interface that the MVCSoft version of javax.jdo.Extent implements.
MVCSoftExtent mvcsoftExtent = (MVCSoftExtent) extent;
mvcsoftExtent.setSubsetTechniqueCursor(5,
ResultSet.TYPE_SCROLL_INSENSITIVE );

Iterator iterator = extent.iterator();

We print out the first 15 rows. Note that with an extent, there are no options beyond
sequential iteration (by design). For most purposes, you should use a query rather than a
plain extent.
int iter = 0;
while ((iter<15) && iterator.hasNext())
{
RabbitObject ro = (RabbitObject) iterator.next();
System.out.println(ro.getSomeVal1());
iter++;
}

extent.close(iterator);

}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
if (persistenceManager.currentTransaction().isActive())

203
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

persistenceManager.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
persistenceManager.close();
}

}
}
Iterating a Query
The following class demonstrates how to manage a large result set while iterating an
Extent, using simple iteration through the result set.
package com.mvcsoft.jdosamples.queries.subsets;

import javax.jdo.*;
import java.util.Properties;
import java.util.List;

import com.mvcsoft.jdo.runtime.user.MVCSoftExtent;

public class QuerySubsetIterMain


{
public static void main(String[] args)
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.queries.subsets.MyMetaData");

properties.setProperty("javax.jdo.option.ConnectionUserName",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionPassword",
"TEST");
properties.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:sapdb://localhost/TST");
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");

PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(properties);
PersistenceManager persistenceManager =
factory.getPersistenceManager();
try
{
Transaction transaction =
persistenceManager.currentTransaction();
transaction.setNontransactionalRead(true);
Extent extent = persistenceManager.getExtent(RabbitObject.class,
true);

204
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Here we configure the extent to use simple iteration, using the MVCSoftExtent interface
that the MVCSoft version of javax.jdo.Extent implements.
MVCSoftExtent mvcsoftExtent = (MVCSoftExtent) extent;

mvcsoftExtent.setSubsetTechnique(MVCSoftExtent.SUBSET_ITERATE_RESULTS,
5);

Query query = persistenceManager.newQuery();


query.setCandidates(extent);
query.setClass(RabbitObject.class);
query.setFilter("someVal1 != null");

The ability to cast the query results to the java.util.List interface is an MVCSoft
extension. The JDO specification allows a cast to java.util.Collection.
List results = (List) query.execute();
The results.size() call will result in a select count(*) statement.
System.out.println("Count:" + results.size());

for (int iter=10; iter<=55; iter++)


Here, persistence capable objects will only be instantiated starting from the tenth object
(as a result of the results.get(iter) call starting with a value of 10).
{
RabbitObject ro = (RabbitObject) results.get(iter);
System.out.println(ro.getSomeVal1());
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
if (persistenceManager.currentTransaction().isActive())
persistenceManager.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
persistenceManager.close(); }

}
}

205
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Level-Two Caching
Basic caching by a PersistenceManager instance within a transaction—or between
transactions using settings such as RetainValues or NontransactionalRead—is part of the
JDO specification. Caching within a transaction is always appropriate, although
depending on your database and its settings, you may need to verify that the state hasn’t
changed between the transactional read and transactional write. (MVCSoft supports this
where necessary through counters, timestamps, or field value comparison.)
Sometimes it can be valuable to cache data between transactions and without tying the
cache to a particular PersistenceManager instance. This is what MVCSoft means by a
level-two cache. MVCSoft provides caching interfaces and semantics for a level-two
cache, and a “reference” implementation of these interfaces. In addition, MVCSoft 2.0
ships with implementations for SwarmCache and Coherence third-party caches.
SwarmCache is an open source (LGPL) cache that uses invalidation for distributed
coordination of the cache; see http://swarmcache.sourceforge.net/ for more information
on this product, or below for more information on its integration with MVCSoft.
Coherence is a commercial cache sold by Tangosol, and is the clear choice for high-end
or demanding caching requirements. See http://www.tangosol.com/index.jsp for more
information on this product, or below for more information on its integration with
MVCSoft.
The MVCSoft JDO product separates the caching implementation from the caching
policy, i.e. what should be cached. Decisions about what should be cached can be made
about classes, fields, and relationships.
If an object is available in the level-two cache, it is always instantiated from that cache—
regardless of how the reference to that object was obtained. However, JDO queries are
always issued against the database and not the cache, so database i/o will occur for
queries even if your entire object model has been cached.
Controlling the Cache Policy
The decision about whether to cache a particular class, field, or relationship is made by a
class associated with the PersistenceManagerFactory that implements the interface
com.mvcsoft.jdo.runtime.cache.CachePolicy. The class is instantiated using the default
constructor based on information provided to the PersistenceManagerFactory at startup
(see the chapter on configuration options for more information).
MVCSoft provides a default implementation that can be used for most purposes:
com.mvcsoft.jdo.runtime.cache.util.CachePolicyProperties. This class consults a
properties resource also provided to the PersistenceManagerFactory. The format of the
properties file is as follows. If a class is not included, it is not cached. To cache a class,
add an entry in the following format:
fields.policy.fullyqualifiedclassname=policy

206
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

where fullyqualifiedclassname is the dot-separated path and class name of your


persistence capable class, and policy is one of cache-all-fields, cache-listed-fields, or
ignore-listed-fields. If it is cache-listed-fields or ignore-listed-fields, the fields are listed
in an additional entry in the properties file, in the format:
fields.list. fullyqualifiedclassname=comma-separated-list-of-fields
where fullyqualifiedclassname is once again the dot-separated path and class name of
your persistence capable class, and comma-separated-list-of-fields is the comma
separated list of fields in that class that should be cached or not cached. (The fields not
listed will have the opposite policy applied.) Note that in the case of inheritance, all
inherited fields must be configured for each concrete class.
You can provide your own CachePolicy implementation. The CachePolicy interface is
defined as follows:
package com.mvcsoft.jdo.runtime.cache;

public interface CachePolicy


{
public boolean cacheObject(int classId);
public boolean cacheField(int classId, int fieldId);
}
Each class and persistent field is assigned an id. The field ids are actually included in the
JDO specification as part of the definition of javax.jdo.PersistenceCapable. The class id
is assigned by the MVCSoft runtime.
A translation between the class name and class id can be made by getting a reference to
the MVCSoft metadata class (com.mvcsoft.jdo.runtime.framework.MetaData) from the
MVCSoft PersistenceManagerFactory implementation class, and calling one of the
following two methods: public int getClassId( Class cl ); or public int getClassId( String
cl ). A translation between a field name and field id can be obtained from the method
javax.jdo.spi.JDOImplHelper.getFieldNames(Class pcClass), or by calling the MVCSoft
metadata class method public int getFieldId( int classId, String fieldName ).
Using MVCSoft’s Reference Level-Two Cache Implementation
The MVCSoft reference level-two cache implementation allows multiple readers or one
writer to access a transactionally consistent cache associated with a single
PersistenceManagerFactory. The cache implements a configurable least-recently-used
ejection policy to control resource usage. To use the MVCSoft cache, set the
PersistenceManagerFactory property com.mvcsoft.jdo.CacheClass to
com.mvcsoft.jdo.runtime.cache.implementations.basicreadwrite.CacheImpl. To set
the maximum number of objects in the cache, set the com.mvcsoft.jdo.cache.max.size
property to a non-zero integer. The default is 1000.

For relationships, only the keys will be stored as part of the parent object’s
cached state.

207
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Caching Interfaces and Semantics


MVCSoft’s intention is to provide interfaces that can be used to adapt other caching
implementations for use with its runtime. Trivial adaptations are easy and potentially
quite useful; for instance, you could provide read-only information from a Java class
rather than hitting the database. If you are interested in a more complex adaptation of a
third-party cache that potentially would be useful to other users, please contact MVCSoft
and we will work with you to ensure the interfaces and their runtime semantics will be
appropriate for the requirements of that cache.
The fundamental Cache interface is defined as follows:
package com.mvcsoft.jdo.runtime.cache;

import javax.transaction.Transaction;

public interface Cache


{
// JDO transaction
public TransactionalCache getTransactionalCache();
// managed environment
public TransactionalCache getTransactionalCache( Transaction
transaction );
}
The second version is called when executing in a managed environment with an external
TransactionManager, so that the transaction can be identified with the
Transaction.hashCode and Transaction.equals methods, if necessary for that
implementation.
The TransactionalCache interface is defined as follows:
package com.mvcsoft.jdo.runtime.cache;

public interface TransactionalCache


{
public LogicalUnitOfWork getLogicalUnitOfWork(Object workType,
Object key, boolean create);
public void prepare()
throws CacheException;
public void commit();
public void rollback();
}
The prepare(), commit(), and rollback() methods aren’t difficult to understand. They are
all called by the MVCSoft runtime. The prepare() method is called before the transaction
completes, but after the changes have been flushed to the database. The commit() and
rollback() methods are called after the transaction is completed. If the cache is
participating in the transaction using XA interfaces, commit() and rollback() notifications
from the MVCSoft runtime would be ignored and prepare() might be ignored. If the
cache implementation is using the callbacks from the MVCSoft JDO runtime, it should

208
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

be aware that program failure between prepare() and commit/rollback is possible, and it
should take this into account.
A logical unit of work is a collection of objects that should be treated for caching
purposes as a single object (to minimize network communication). Depending on the
application, a good example of this might be an order and its line items. The MVCSoft
1.0 release treats each object as its own logical unit of work, but future releases will
provide additional configuration options. The getLogicalUnitOfWork method takes three
parameters: an instance of a java.lang.Object that uniquely identifies a particular
persistence capable class hierarchy (and may be ignored by the Cache implementation if
it is not useful to partition the request in this way); an instance of java.lang.Object that
uniquely identifies the logical unit of work (by providing the key of its “primary”
persistence capable object); and a boolean “create” parameter which is “false” for
requests that will read from the cache and “true” for requests that will write to it. It
returns an instance of the following interface:
package com.mvcsoft.jdo.runtime.cache;
public interface LogicalUnitOfWork
{
public static final int CACHE_ACTION_MODIFY = 0;
public static final int CACHE_ACTION_NONE = 1;

public void setCacheAction(int action);


public void setIncrementedLockObject(Object obj);
public FieldWriter addObjectToLogicalUnitOfWork(Object key);
public FieldReader getObjectFromLogicalUnitOfWork(Object key);
public void removeObjectFromLogicalUnitOfWork(Object key);
public void deleteLogicalUnitOfWork();
}
The logical unit of work has two kinds of writes: those that are actual modifications, and
those that are the JDO runtime’s effort to provide the cache with information that it didn’t
have. The setCacheAction method distinguishes between these two types of activities for
the entire cache. This information might be used by the cache implementation to
determine what type of locking might be appropriate. The legal values are
CACHE_ACTION_MODIFY and CACHE_ACTION_NONE (the default). The JDO
runtime will set these values; the cache implementation just consumes them.
The setIncrementedLockObject method sets the timestamp or counter value used by the
MVCSoft runtime for optimistic locking. It might be used by the caching implementation
to resolve locking conflicts, just like it is used in the database. The semantics of this
method are not yet implemented by the JDO runtime (it is not called).
The addObjectToLogicalUnitOfWork method is called by the runtime when it needs to
update the cache associated with a particular object—either because of changes to that
object, or because the information wasn’t already in the cache. (In the 1.0 version of the
MVCSoft runtime, there is a one-one correspondence between an object and a logical
unit of work.) This method is called even if the object is already part of the logical unit of

209
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

work, or already exists. The returned FieldWriter interface is used to actually write the
new information.
The getObjectFromLogicalUnitOfWork method is called by the runtime when it tries
to read information from the cache about a particular object. The returned FieldReader
interface is used to actually read the information. If there is no information about that
object in the logical unit of work, null is returned.
The removeObjectFromLogicalUnitOfWork would only be called if there were a one-
many relationship between a logical unit of work and persistence-capable objects (which
there can’t be in version 1.0). If the “primary” object for that logical unit of work is
deleted, the method deleteLogicalUnitOfWork should be called instead.
The deleteLogicalUnitOfWork method is called when the primary object for that logical
unit of work is deleted from the database. The caching system no longer needs to manage
that element of the cache (including any secondary objects associated with the logical
unit of work).
The MVCSoft JDO runtime calls the FieldWriter interface to write persistent state to the
cache—both simple fields and relationship values. If a field is mapped to multiple
columns, it will be written as a “multipart field,” using the method
writeMultiPartStateField and in the order returned from the MappingTransformer method
stateToDb(). Relationships to other persistent capable objects (from single-valued fields,
or from java.util.Collection or java.util.Map instances) are written with just their keys.
The FieldWriter interface is as follows:
package com.mvcsoft.jdo.runtime.cache;

import java.util.Collection;

public interface FieldWriter


{
public void writeStateField( int fieldId, Object value);
public void writeMultiPartStateField( int fieldId, Object[] values);

public void addCollectionMember( int fieldId, Object key, Object


value );
public void removeCollectionMember( int fieldId, Object key );
public void setCollectionsObjectEmpty( int fieldId );

public void addMapKeyValuePair( int fieldId, Object key, Object value


);
public void removeMapKeyValuePair( int fieldId, Object key );
public void setMapObjectEmpty( int fieldId );

public void setObjectField( int fieldId, Object key);

public void rewriteCollection( int fieldId, Collection keyValuePairs


);
public void rewriteMap( int fieldId, Collection keyValuePairs );

210
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

The MVCSoft JDO runtime calls the FieldReader interface to read persistent state from
the cache. The isCached method is necessary to distinguish between a null field and a
field that isn’t in the cache. The FieldReader interface is declared as follows:
package com.mvcsoft.jdo.runtime.cache;

import java.util.Collection;
import java.util.List;

public interface FieldReader


{
public boolean isCached( int fieldId );

public Object readObjectField( int fieldId );


public Object[] readMultiPartObjectField( int fieldId );

public List getCollectionFieldKeyValuePairs( int fieldId );


public Collection getMapFieldKeyValuePairs( int fieldId );
public Object getObjectField( int fieldId );
}

SwarmCache Cache Integration


The SwarmCache cache maintains coherence via what has become known as the Seppuku
pattern. When an obect is changed, a notification is broadcast and it is simply removed
from every factory’s second level cache. When it is next read, the new value will be
recached for that factory. Note that this is not a transactional cache. There is no
transactional locking, and there are no guarantees that data will not be stale. (You can
place limits on how stale the data might be by configuring a time-out.)
The following properties are used to configure the integration between the MVCSoft JDO
PersistenceManagerFactory and the SwarmCache cache:
com.mvcsoft.jdo.CacheClass – this should be given a value of
com.mvcsoft.jdo.runtime.cache.implementations.swarmcache.SwarmCacheImpl
net.sf.swarmcache.CacheType – this sets the value for the
net.sf.swarmcache.CacheConfiguration’s cacheType property. The default value is
CacheConfiguration.TYPE_HYBRID
net.sf.swarmcache.LRUCacheSize – this sets the value for the
net.sf.swarmcache.CacheConfiguration’s LRUCacheSize property. The default value is
CacheConfiguration. DEFAULT_LRU_CACHE_SIZE
net.sf.swarmcache.MulticastIP – this sets the value for the
net.sf.swarmcache.CacheConfiguration’s multicastIP property. The default value is
CacheConfiguration. DEFAULT_MULTICAST_IP
net.sf.swarmcache.ChannelProperties – this sets the value for the
net.sf.swarmcache.CacheConfiguration’s channelProperties property. If no value is
specified, the configuration method isn’t called

211
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

net.sf.swarmcache.CacheName – this sets the name of the cache used in the


net.sf.swarmcache.CacheFactory.createCache call. The default name of the cache is
mvcsoft-jdo.
Coherence Cache Integration
Tangosol’s Coherence is an obvious choice for high-end caching needs, and probably
should be considered any time you have volume that requires more than one server for an
application. It can can be configured to provide required transactional guarantees (or to
improve concurrency at the expense of these guarantees). It can offer distributed or
replicated caches; high availability and fault tolerance; high performance; and excellent
scalability.
Building the MVCSoft-Coherence Integration Module
The source code to MVCSoft’s Coherence integration module is included with the rest of
MVCSoft’s source code. However, it requires Coherence libraries to build, and so is not
part of the default build. To build the module from source, just put tangosol.jar and
coherence.jar into the lib directory and run the “coherence” target for the MVCSoft ant
build (type “ant coherence” from the MVCSoft src directory). The mvcsoft_coherence.jar
file will be created in the builds directory.
Configuration Strategies
MVCSoft’s Coherence integration module provides for three general cache update
strategies: MVCSoft’s “best effort” versioned cache; cooperation with Tangosol’s
CacheAdapter to communicate with their deployed RAR (connector specification
resource archive) in a managed environment; and cooperation with Tangosol’s local
transaction management facilities (accessed via CacheFactory.getLocalTransaction).
Here are some of the differences for these strategies:

MVCSoft “Best Tangosol Tangosol Local


Effort” CacheAdapter/RAR Transaction

Additional config None Concurrency, Concurrency,


options isolation, timeout, isolation, timeout,
verification strategy verification strategy

Type of commit Update during 2-phase 2-phase


afterCompletion (prepare/commit) (prepare/commit)
callback

Action on cache Data cleared and Rollback Rollback


update conflict version incremented

Possibility of No (if no other Yes Yes


deadlock application is
acquiring locks)

212
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

acquiring locks)

Cache publishing MVCSoft runtime Coherence runtime MVCSoft calls to


callbacks managed through JCA Coherence facade
by:

Can be used in: Standalone and Managed Standalone and


managed environments managed
environments environments

Cache Isolation and Concurrency


Because the MVCSoft JDO runtime maintains its own cache within a transaction (as is
required by the JDO specification), it always has an isolation level of at least “repeatable
read” with any second-level cache (assuming that nothing is done to discard this cached
state, such as PersistenceManager.evict or PersistenceManager.refresh). Coherence
supports read-committed and repeatable-read strategies for its cache—which in practice
usually work out the same in a JDO application. In addition, Coherence offers a serialized
isolation level that prevents phantom reads. (The serializable level of isolation in the
cache—and presumably the database—is costly in terms of concurrency and rarely
required. MVCSoft typically recommends applications be written assuming read-
committed isolation.)
The MVCSoft “best effort” strategy uses an optimistic concurrency strategy. This means
that versioning, rather than locking, is used to maintain data integrity in an environment
with concurrent updates. Versions are only checked on writes for the cache; if versions
need to be checked on reads because of application requirements, this can be done against
the database.
Coherence offers both a pessimistic and an optimistic concurrency strategy. The
pessimistic strategy is supported by MVCSoft but is not recommended. Because a cache
lock in Coherence will not prevent “gets” (just “puts”), the only safe way to use a
pessimistic strategy and prevent the “lost update” problem is to require a consistent lock-
>get->put->unlock sequence. There is currently no API to lock a JDO object. In fact, the
JDO runtime bunches updates in the transaction together and only writes them out during
a flush (before a query or at transaction end), and objects will only be pessimistically
locked at this time even if written.
Coherence’s optimistic concurrency strategy is a different story. It prevents the lost
update problem in the same way MVCSoft’s “best effort” strategy does: through
versioning. It also provides several capabilities that MVCSoft’s “best effort” strategy
does not: consistency (via the locking of objects as a set) and versioning of read-only
objects. MVCSoft provides special support of this strategy through a custom
implementation of the TransactionMap.Validator interface, which compares version
stamps placed on objects by the MVCSoft runtime rather than the entire object state.

213
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Configuration Options
Configuration of the Coherence cache is done outside the MVCSoft runtime. These are
options for the MVCSoft-Coherence integration module only:
com.tangosol.net.CacheName – this value is used as a parameter to the
com.tangosol.net.CacheFactory.getCache method. The default value is mvcsoft-jdo
com.tangosol.net.TransactionalCache – this value tells the MVCSoft JDO runtime
whether or not the named cache is transactional. If true, the runtime updates the cache
during the lifetime of the transaction (during a flush). If false, the runtime updates the
cache after the transaction has committed. The default is false.
com.tangosol.net.UsesResourceAdapter—this value tells the MVCSoft runtime
whether a transactional cache is accessed via a CacheAdapter to communicate with a
connection factory (RAR module), true, or whether it is managed as a local transaction,
false. The default is false.
com.tangosol.net.ResourceAdapterJNDIName—this value tells the MVCSoft runtime
the JNDI name to access the Coherence cache connection factory. It is passed as a
parameter to the CacheAdapter. The default is tangosol.coherenceTx, which is also the
default name in the Coherence RAR
com.tangosol.net.Isolation—this value sets the isolation level for a transactional cache.
It can be one of get_committed, repeatable_get, or serializable. The default is
get_committed
com.tangosol.net.Concurrency—this value sets the concurrency strategy for a
transactional cache. It can be optimistic or pessimistic. The default (and recommended)
value is optimistic
com.tangosol.net.Timeout—this value sets the transaction timeout for a transactional
cache. The default is 0 (no timeout)
com.tangosol.net.Validator—this value sets the class for validation of an optimistic
transaction. The default is null; if you are using an optimistic transaction you should set it
to com.mvcsoft.jdo.runtime.cache.implementations.coherence.BasicValidator or your
own class

214
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

Using MVCSoft JDO with an Application Server


The JDO specification anticipates two primary scenarios for usage: two tier and three tier.
The two tier usage scenario is for a standalone application. The three-tier usage scenario
is for managed environments (i.e. application servers). The MVCSoft JDO Toolkit
supports both scenarios. Until now, the examples have been for two-tier usage. This
chapter explains how to set up MVCSoft for use with an application server, and how JDO
usage differs in the three-tier scenario.

The three tiers referred to in these usage scenarios are the client, the
application server, and the database. In the two-tier scenario, the JDO
PersistenceManager instance resides on the client. In the three-tier
scenario, the PersistenceManager instance resides in the application
server.

J2EE Application Server Usage


There are two basic differences in how you use JDO in an application server. First,
PersistenceManager instances are used like any other managed resource, in the acquire-
use-release pattern. Second, JDO transactions are tied to application server transactions.
Apart from these two differences, two-tier and three-tier usage is identical.
The Acquire-Use-Release Pattern
In a J2EE application server, all managed resources should used the same way. First, a
factory for that resource is acquired from the JNDI namespace. This factory can be
cached in the bean instance between invocations to improve performance—just like
a home interface might be. Second, an instance of the resource is acquired from the
factory. This instance is appropriate for the transaction in effect at the time. Finally, in the
same business method as the resource was acquired, it must be closed in a finally block.
This is an example of that pattern:
PersistenceManager persistenceManager = null;
try
{
Acquire a factory from the JNDI namespace.
Context ctx = new InitialContext();
PersistenceManagerFactory factory = (PersistenceManagerFactory)
ctx.lookup("java:comp/env/jdo/jdofactory");
Get an instance of the PersistenceManager from the factory.
persistenceManager = factory.getPersistenceManager();
Use the PersistenceManager.
MyPersistentObject obj = new MyPersistentObject();
persistenceManager.makePersistent(obj);

215
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

}
Close the PersistenceManager in a finally block.
finally
{
if (persistenceManager != null)
{
try
{
if (persistenceManager.currentTransaction().isActive())
persistenceManager.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
persistenceManager.close();
}
}
In a managed environment, the PersistenceManagerFactory will pool PersistenceManager
instances so that the same PersistenceManager will be returned every time
getPersistenceManager() is called on the factory within a particular transaction. The
PersistenceManager.close() method does not close the PersistenceManager in a managed
environment, but instead decrements a usage count. The PersistenceManager won’t be
closed until the transaction commits.

There are alternative scenarios to the acquire-use-release pattern. For


instance, a resource might be cached by a stateful session bean or in a
servlet session. The J2EE, EJB, and JDO specifications allow this, but few
use cases have been found where this is a good idea.

Transactional Usage
In a two-tiered environment, JDO transactions are controlled by calling begin(),
commit(), and rollback() on the javax.jdo.Transaction instance. In a three-tiered
environment, transactions are managed by the application server, and the JDO runtime is
controlled by the application server’s transaction.
If the transaction demarkation is controlled by the container (e.g. with EJB container-
managed transactions), the JDO developer does not use the begin(), commit(), or
rollback() methods at all. If the transaction demarkation is controlled by the developer,
either the javax.transaction.UserTransaction instance can be used (as described in the
J2EE specification), or the javax.jdo.Transaction instance can be used—which will just in
turn delegate the call to javax.transaction.UserTransaction.
Enterprise JavaBeans
There are multiple scenarios described by the JDO specification where JDO can be used
with EJBs. They are:

216
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

♦ Stateless Session Bean with Container Managed Transactions


♦ Stateful Session Bean with Container Managed Transactions
♦ Stateless Session Bean with Bean Managed Transactions
♦ Stateful Session Bean with Bean Managed Transactions
♦ BMP Entity Beans
(It is also possible to write a CMP entity bean implementation using JDO, but this is
outside the scope of this document and is a tool-provider, not end-user, task.)
MVCSoft recommends that stateless session beans be used as a façade to your JDO
functionality. Entity beans add unnecessary overhead by providing services (such as
declarative security and distribution) that usually should not be necessary for your
business object persistent model. Stateful session beans will usually have the same
semantics as stateless session beans. However, stateful session beans with bean-managed
transactions can keep a PersistenceManager open between business method calls. This is
rarely a good idea.
Setting Up the MVCSoft Runtime for Application Server
Usage
There are four application-server-specific steps that you should take to set up an
MVCSoft JDO application for use in a J2EE application server. They are:
1) Configure the PersistenceManagerFactory property
com.mvcsoft.jdo.ManagedEnvironment to true, as documented in the
configuration information for the PersistenceManagerFactory
2) Configure the PersistenceManagerFactory properties for the transactional and
non-transactional datasources: javax.jdo.option.ConnectionFactoryName and
javax.jdo.option.ConnectionFactory2Name
3) Configure the PersistenceManagerFactory so that it can find an instance of the
transaction from the managed environment, , as documented in the configuration
information for the PersistenceManagerFactory. If you can use the MVCSoft-
supplied JNDITransactionRetriever instance, also configure the JNDI name of the
TransactionManager appropriate for your application server
4) Install the MVCSoft PersistenceManagerFactory in your application’s JNDI
namespace using the vendor-specific configuration capabilities

The JDO specification anticipates the use of the J2EE Connector


Architecture “caching manager” mechanism to integrate a JDO
implementation with an application server. However, in practice the
caching manager mechanism is underspecified. This quote from the
proposed final draft of the J2EE Connector Architecture specification
version 1.5 (the latest available as of August 15, 2003) illustrates the issue:

217
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

“This section serves as a brief introduction to the caching support in the


connector architecture. A future version of the connector architecture will
address this issue in detail.”

Installing the MVCSoft PersistenceManagerFactory in the


JNDI Namespace
The standard way to get the PersistenceManagerFactory instance is from JNDI. The
mechanism for installing it there in the first place is vendor-specific. This documentation
will provide two examples: JBoss 3.2.x (using a SAR), and WebLogic 7.0 (using a start-
up class).
Using a JBoss SAR
JBoss allows “service archives” (SARs) to be deployed as part of an application EAR
file. These SARs can install additional services into the running JBoss application server
as MBean components. In this case, we can use a SAR file to configure and instantiate a
PersistenceManagerFactory and bind it to the JNDI namespace. Please note that the
specifics of the JBoss application server, MBeans, and the SAR format are beyond the
scope of this documentation. Consult the JBoss documentation for additional information
if desired.
There are three files in the SAR (which, like a JAR, is simply a file in ZIP format): an
MBean interface (com.mvcsoft.jdosamples.j2ee.jboss.MVCSoftJDOJNDIMBean); an
implementation class (com.mvcsoft.jdosamples.j2ee.jboss.MVCSoftJDOJNDI); and a
deployment descriptor (META-INF/jboss-service.xml). The SAR is included in the
application.xml descriptor—and in the EAR file—as if it were an EJB. The
application.xml file for the EAR might look like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE
Application 1.2//EN"
"http://java.sun.com/j2ee/dtds/application_1_2.dtd">
<application>
<display-name>MVCSoft EJB JDO Sample</display-name>
<module>
<ejb>test_ejbs.jar</ejb>
</module>
<module>
<ejb>mvcsoft_jdo.sar</ejb>
</module>
</application>
The MBean Interface
The majority of these get/set methods are for configurations of the MVCSoft
PersistenceManagerFactory instance. They can be set in the deployment descriptor,
making for an easy configuration. Here you can also set the JNDI name to which the

218
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

factory instance will be bound. The start and stop methods are names that have special
meaning to the JBoss runtime, and these lifecycle methods will be the triggers for the
code to create and bind the factory (start), or remove the factory during undeploy (stop).
package com.mvcsoft.jdosamples.j2ee.jboss;

public interface MVCSoftJDOJNDIMBean


{
public String getJndiName();
public void setJndiName(String jndiName);

public String getConnectionFactoryName();


public void setConnectionFactoryName(String connectionFactoryName);

public String getConnectionFactory2Name();


public void setConnectionFactory2Name(String connectionFactory2Name);

public String getOptimistic();


public void setOptimistic(String optimistic);

public String getRetainValues();


public void setRetainValues(String retainValues);

public String getRestoreValues();


public void setRestoreValues(String restoreValues);

public String getIgnoreCache();


public void setIgnoreCache(String ignoreCache);

public String getNontransactionalRead();


public void setNontransactionalRead(String nontransactionalRead);

public String getLogFactoryClass();


public void setLogFactoryClass(String logFactoryClass);

public String getParameterLoggerName();


public void setParameterLoggerName(String parameterLoggerName);

public String getStatementLoggerName();


public void setStatementLoggerName(String statementLoggerName);

public String getFunctionMap();


public void setFunctionMap(String functionMap);

public String getCacheClass();


public void setCacheClass(String cacheClass);

public String getCachePolicyClass();


public void setCachePolicyClass(String cachePolicyClass);

public String getCacheMaxSize();


public void setCacheMaxSize(String cacheMaxSize);

public String getCachePolicyProperties();

219
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public void setCachePolicyProperties(String cachePolicyProperties);

public String getMetadataName();


public void setMetadataName(String metadataName);

public void start() throws Exception;


public void stop() throws Exception;
}
The Implementation Class
This class caches the property values that are set by the JBoss runtime from the values we
provide in the SAR deployment descriptor. During the start() method, we use these
values to configure the PersistenceManagerFactory and add it to the JNDI namespace.
Note that this will add it to the global namespace, where it is directly accessible by the
deployed components. However, you can also create a symbolic link to this global name
from a name located in a component’s java:comp/env/ namespace.
package com.mvcsoft.jdosamples.j2ee.jboss;

import com.mvcsoft.jdosamples.j2ee.jboss.MVCSoftJDOJNDIMBean;

import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import java.util.Properties;

import org.jboss.naming.NonSerializableFactory;

public class MVCSoftJDOJNDI implements MVCSoftJDOJNDIMBean


{
private String jndiName;
private String metadataName;
private String connectionFactoryName;
private String connectionFactory2Name;
private String optimistic;
private String retainValues;
private String restoreValues;
private String ignoreCache;
private String nontransactionalRead;
private String logFactoryClass;
private String parameterLoggerName;
private String statementLoggerName;
private String functionMap;
private String cacheClass;
private String cachePolicyClass;
private String cacheMaxSize;
private String cachePolicyProperties;

private PersistenceManagerFactory factory;

public String getJndiName()

220
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
return jndiName;
}

public void setJndiName(String jndiName)


{
this.jndiName = jndiName;
}

public String getConnectionFactoryName()


{
return connectionFactoryName;
}

public void setConnectionFactoryName(String connectionFactoryName)


{
this.connectionFactoryName = connectionFactoryName;
}

public String getConnectionFactory2Name()


{
return connectionFactory2Name;
}

public void setConnectionFactory2Name(String connectionFactory2Name)


{
this.connectionFactory2Name = connectionFactory2Name;
}

public String getOptimistic()


{
return optimistic;
}

public void setOptimistic(String optimistic)


{
this.optimistic = optimistic;
}

public String getRetainValues()


{
return retainValues;
}

public void setRetainValues(String retainValues)


{
this.retainValues = retainValues;
}

public String getRestoreValues()


{
return restoreValues;
}

221
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public void setRestoreValues(String restoreValues)


{
this.restoreValues = restoreValues;
}

public String getIgnoreCache()


{
return ignoreCache;
}

public void setIgnoreCache(String ignoreCache)


{
this.ignoreCache = ignoreCache;
}

public String getNontransactionalRead()


{
return nontransactionalRead;
}

public void setNontransactionalRead(String nontransactionalRead)


{
this.nontransactionalRead = nontransactionalRead;
}

public String getLogFactoryClass()


{
return logFactoryClass;
}

public void setLogFactoryClass(String logFactoryClass)


{
this.logFactoryClass = logFactoryClass;
}

public String getParameterLoggerName()


{
return parameterLoggerName;
}

public void setParameterLoggerName(String parameterLoggerName)


{
this.parameterLoggerName = parameterLoggerName;
}

public String getStatementLoggerName()


{
return statementLoggerName;
}

public void setStatementLoggerName(String statementLoggerName)


{
this.statementLoggerName = statementLoggerName;
}

222
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public String getFunctionMap()


{
return functionMap;
}

public void setFunctionMap(String functionMap)


{
this.functionMap = functionMap;
}

public String getCacheClass()


{
return cacheClass;
}

public void setCacheClass(String cacheClass)


{
this.cacheClass = cacheClass;
}

public String getCachePolicyClass()


{
return cachePolicyClass;
}

public void setCachePolicyClass(String cachePolicyClass)


{
this.cachePolicyClass = cachePolicyClass;
}

public String getCacheMaxSize()


{
return cacheMaxSize;
}

public void setCacheMaxSize(String cacheMaxSize)


{
this.cacheMaxSize = cacheMaxSize;
}

public String getCachePolicyProperties()


{
return cachePolicyProperties;
}

public void setCachePolicyProperties(String cachePolicyProperties)


{
this.cachePolicyProperties = cachePolicyProperties;
}

public String getMetadataName()


{
return metadataName;

223
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

public void setMetadataName(String metadataName)


{
this.metadataName = metadataName;
}

This life-cycle callback is where most of the work of this component is done. We’ve
hard-coded a few values: the transaction manager JNDI name (for the JBoss default); the
MVCSoft-appropriate value for javax.jdo.PersistenceManagerFactoryClass; and the
property that indicates we are operating in a managed environment.
public void start() throws Exception
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.ManagedEnvironment",
"true");

properties.setProperty("com.mvcsoft.jdo.TransactionManagerJNDIName",
"java:/TransactionManager");

properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
metadataName);

if (connectionFactoryName != null)
properties.setProperty("javax.jdo.option.ConnectionFactoryName",
connectionFactoryName);
if (connectionFactory2Name != null)
properties.setProperty("javax.jdo.option.ConnectionFactory2Name",
connectionFactory2Name);
if (optimistic != null)
properties.setProperty("javax.jdo.option.Optimistic",
optimistic);
if (retainValues != null)
properties.setProperty("javax.jdo.option.RetainValues",
retainValues);
if (restoreValues != null)
properties.setProperty("javax.jdo.option.RestoreValues",
restoreValues);
if (ignoreCache != null)
properties.setProperty("javax.jdo.option.IgnoreCache",
ignoreCache);
if (nontransactionalRead != null)
properties.setProperty("javax.jdo.option.NontransactionalRead",
nontransactionalRead);
if (logFactoryClass != null)
properties.setProperty("com.mvcsoft.jdo.LogClassFactory",
logFactoryClass);
if (parameterLoggerName != null)
properties.setProperty("com.mvcsoft.jdo.ParameterLoggerName",
parameterLoggerName);

224
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

if (statementLoggerName != null)
properties.setProperty("com.mvcsoft.jdo.StatementLoggerName",
statementLoggerName);
if (functionMap != null)
properties.setProperty("com.mvcsoft.jdo.FunctionMap",
functionMap);
if (cacheClass != null)
properties.setProperty("com.mvcsoft.jdo.CacheClass", cacheClass);
if (cachePolicyClass != null)
properties.setProperty("com.mvcsoft.jdo.CachePolicyClass",
cachePolicyClass);
if (cacheMaxSize != null)
properties.setProperty("com.mvcsoft.jdo.cache.max.size",
cacheMaxSize);
if (cachePolicyProperties != null)
properties.setProperty("com.mvcsoft.jdo.cache.policy.properties",
cachePolicyProperties);
We instantiate the factory here.

factory = JDOHelper.getPersistenceManagerFactory(properties);
The rebind() helper method actually binds the factory to the namespace.

rebind();
}
This method just cleans up the namespace when the EAR is undeployed or JBoss shut
down.

public void stop() throws Exception


{
unbind(jndiName);
factory = null;
}

private void rebind()


{
try
{
InitialContext rootCtx = new InitialContext();
Name fullName = rootCtx.getNameParser("").parse(jndiName);
System.out.println("fullName=" + fullName);
This JBoss helper class allows us to bind the unserialized form of the factory to the JNDI
namespace. Technically, the PersistenceManagerFactory class is serializable. However,
when it is serialized it loses information such as its pool of PersistenceManager instances
and potentially its second level cache. (The JDO specification requires that the same
instance of the PersistenceManagerFactory be returned for all EJBs sharing the same
datastore resources.)

225
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

NonSerializableFactory.rebind(fullName, factory, true);


}
catch (NamingException e)
{
e.printStackTrace();
}
}

private void unbind(String jndiName)


{
try
{
InitialContext rootCtx = new InitialContext();
rootCtx.unbind(jndiName);
NonSerializableFactory.unbind(jndiName);
}
catch (NamingException e)
{
e.printStackTrace();
}
}
}
The SAR Deployment Descriptor
Here we set some of the properties implemented above by using the SAR deployment
descriptor. The MBean name just needs to be unique:
<server>
<mbean code="com.mvcsoft.jdosamples.j2ee.jboss.MVCSoftJDOJNDI"
name="mvcsoft.jdo:service=test">
<attribute name="JndiName">java:/jdofactory</attribute>
<attribute name="ConnectionFactoryName">java:/DefaultDS</attribute>
<attribute
name="MetadataName">com.mvcsoft.testejb.Metadata</attribute>
<depends>jboss:service=Naming</depends>
<depends>jboss:service=Transaction</depends>
</mbean>
</server>
You can pretty much use this SAR as-is (except for the properties in the SAR deployment
descriptor) with all your JBoss deployments.
Using a WebLogic Startup Class
WebLogic provides a proprietary mechanism for performing actions on server startup.
This mechanism can be used to deploy a PersistenceManagerFactory in the JNDI
namespace. Please note that fully documenting the WebLogic startup class mechanism is
beyond the scope of this manual. Consult the WebLogic documentation for more
information.
A WebLogic startup class has a method with the signature public String startup(String s,
Hashtable hashtable) throws Exception. The hashtable contains configuration
information that can be set in the administration console of WebLogic. This sample

226
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

startup class uses two properties from the hashtable: jndiName and propertiesPath. The
jndiName is simply the name to which the PersistenceManagerFactory should be bound
in the root-level subcontext “jdo”. The propertiesPath is the fully-qualified resource name
of the properties file in the application that will be used to configure the
PersistenceManagerFactory when it is instantiated. Note that the factory will be lazily
instantiated at the first access by the MVCSoftJNDIFactory class—because otherwise the
metadata class, properties file, and persistence-capable classes will not be accessible. (A
poor alternative is to put them on the system classpath, but this is not good practice.)
Note that we bind a javax.naming.Reference, rather than binding the factory directly into
the JNDI namespace. This is a standard technique for JNDI and can be used with servers
other than WebLogic. Consult the source for the
com.mvcsoft.jdo.runtime.user.MVCSoftJNDIFactory if you are interested in the details
of this class. Here is the WebLogic startup class:
package com.mvcsoft.weblogicstartup;

import weblogic.common.T3StartupDef;
import weblogic.common.T3ServicesDef;

import javax.naming.*;
import java.util.Hashtable;
import java.util.Set;
import java.util.Iterator;

import com.mvcsoft.jdo.runtime.user.MVCSoftJNDIFactory;

public class MVCSoftJDOStartup implements T3StartupDef


{
public String startup(String s, Hashtable hashtable) throws Exception
{
String jndiName = (String)hashtable.get("jndiName");
String propertiesPath = (String)hashtable.get("propertiesPath");

InitialContext initial = new InitialContext();


Reference ref = new Reference("MyJndiFactoryClass",
new StringRefAddr(MVCSoftJNDIFactory.ADDR_JDOFACTORYNAME,
jndiName),
MVCSoftJNDIFactory.class.getName(),
null);
ref.add(new StringRefAddr(
MVCSoftJNDIFactory.ADDR_JDOPROPERTIES,
propertiesPath));

Context ctx = initial.createSubcontext("jdo");


ctx.bind(jndiName, ref);

return "MVCSoft JDO PersistenceManagerFactory bound to " +


jndiName;
}

public void setServices(T3ServicesDef t3ServicesDef)

227
MVCSoft Inc.
www.mvcsoft.com

MVCSoft JDO Toolkit ©2000-2004, MVCSoft Inc.


All Rights Reserved

{
}
}

228

Вам также может понравиться