Академический Документы
Профессиональный Документы
Культура Документы
www.mvcsoft.com
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
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
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.
3
MVCSoft Inc.
www.mvcsoft.com
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
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
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
7
MVCSoft Inc.
www.mvcsoft.com
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).
8
MVCSoft Inc.
www.mvcsoft.com
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
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.
10
MVCSoft Inc.
www.mvcsoft.com
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.
11
MVCSoft Inc.
www.mvcsoft.com
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;
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();
}
12
MVCSoft Inc.
www.mvcsoft.com
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();
}
}
}
13
MVCSoft Inc.
www.mvcsoft.com
import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;
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;
}
return factory.getPersistenceManager();
}
14
MVCSoft Inc.
www.mvcsoft.com
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
package com.mvcsoft.jdosamples.basics.simplest;
import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;
import java.util.Collection;
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;
}
return factory.getPersistenceManager();
}
16
MVCSoft Inc.
www.mvcsoft.com
import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;
import java.util.Collection;
import java.util.ArrayList;
17
MVCSoft Inc.
www.mvcsoft.com
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;
}
return factory.getPersistenceManager();
}
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();
18
MVCSoft Inc.
www.mvcsoft.com
import javax.jdo.*;
import java.util.Properties;
import java.util.Iterator;
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
return factory.getPersistenceManager();
}
20
MVCSoft Inc.
www.mvcsoft.com
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 BaseClass()
{
}
21
MVCSoft Inc.
www.mvcsoft.com
public BranchAMiddle()
{
}
public BranchBMiddle()
{
}
22
MVCSoft Inc.
www.mvcsoft.com
{
super(baseClassField);
BranchBMiddleField = branchBMiddleField;
}
}
Here is the first of four most-derived classes:
package com.mvcsoft.jdosamples.basics.inheritance;
public BranchAMostDerivedLeft()
{
}
}
Here is the second of four most-derived classes:
package com.mvcsoft.jdosamples.basics.inheritance;
23
MVCSoft Inc.
www.mvcsoft.com
public BranchAMostDerivedRight()
{
}
}
Here is the third of four most-derived classes:
package com.mvcsoft.jdosamples.basics.inheritance;
public BranchBMostDerivedLeft()
{
}
24
MVCSoft Inc.
www.mvcsoft.com
}
Here is the fourth of four most-derived classes:
package com.mvcsoft.jdosamples.basics.inheritance;
public BranchBMostDerivedRight()
{
}
import javax.jdo.*;
import java.util.Properties;
25
MVCSoft Inc.
www.mvcsoft.com
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;
}
return factory.getPersistenceManager();
}
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
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;
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
return factory.getPersistenceManager();
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
}
finally
{
28
MVCSoft Inc.
www.mvcsoft.com
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;
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;
}
29
MVCSoft Inc.
www.mvcsoft.com
{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());
return factory.getPersistenceManager();
}
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
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;
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
return properties;
}
return factory.getPersistenceManager();
}
32
MVCSoft Inc.
www.mvcsoft.com
import javax.jdo.*;
import java.util.Properties;
import java.util.Calendar;
import java.util.Date;
import java.text.DateFormat;
33
MVCSoft Inc.
www.mvcsoft.com
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");
return properties;
}
return factory.getPersistenceManager();
}
34
MVCSoft Inc.
www.mvcsoft.com
// 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
import java.util.Collection;
public SimpleParent()
{
}
36
MVCSoft Inc.
www.mvcsoft.com
}
This is the child class that particpates in the relationships:
package com.mvcsoft.jdosamples.basics.relationships;
public SimpleChild()
{
37
MVCSoft Inc.
www.mvcsoft.com
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
import javax.jdo.JDOException;
import java.util.Properties;
import java.util.LinkedList;
return properties;
}
return factory.getPersistenceManager();
}
38
MVCSoft Inc.
www.mvcsoft.com
39
MVCSoft Inc.
www.mvcsoft.com
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;
return properties;
}
return factory.getPersistenceManager();
}
40
MVCSoft Inc.
www.mvcsoft.com
41
MVCSoft Inc.
www.mvcsoft.com
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.*;
return properties;
}
return factory.getPersistenceManager();
}
42
MVCSoft Inc.
www.mvcsoft.com
43
MVCSoft Inc.
www.mvcsoft.com
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;
import javax.jdo.JDOHelper;
public SampleKeyObject()
44
MVCSoft Inc.
www.mvcsoft.com
{
}
public SampleValueObject()
{
45
MVCSoft Inc.
www.mvcsoft.com
}
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
return properties;
}
return factory.getPersistenceManager();
}
47
MVCSoft Inc.
www.mvcsoft.com
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.*;
48
MVCSoft Inc.
www.mvcsoft.com
properties.setProperty("javax.jdo.option.ConnectionDriverName",
"com.sap.dbtech.jdbc.DriverSapDB");
return properties;
}
return factory.getPersistenceManager();
}
49
MVCSoft Inc.
www.mvcsoft.com
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.*;
return properties;
}
50
MVCSoft Inc.
www.mvcsoft.com
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());
return factory.getPersistenceManager();
}
51
MVCSoft Inc.
www.mvcsoft.com
}
}
52
MVCSoft Inc.
www.mvcsoft.com
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
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
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
56
MVCSoft Inc.
www.mvcsoft.com
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
public SimpleObjForQuery()
{
}
58
MVCSoft Inc.
www.mvcsoft.com
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;
return properties;
}
return factory.getPersistenceManager();
}
pm.currentTransaction().commit();
}
59
MVCSoft Inc.
www.mvcsoft.com
}
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;
60
MVCSoft Inc.
www.mvcsoft.com
return properties;
}
return factory.getPersistenceManager();
}
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);
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
catch (Exception e)
{
// log this exception
}
pm.close();
}
}
This is just a utility method that prints out the results of the query.
62
MVCSoft Inc.
www.mvcsoft.com
}
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
import java.util.Date;
public ParentObjForQuery()
{
}
64
MVCSoft Inc.
www.mvcsoft.com
this.theDate = theDate;
}
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
import javax.jdo.JDOException;
import java.util.Properties;
import java.util.Calendar;
return properties;
}
65
MVCSoft Inc.
www.mvcsoft.com
{
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(getStandardProperties());
return factory.getPersistenceManager();
}
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
}
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.*;
return properties;
}
return factory.getPersistenceManager();
}
67
MVCSoft Inc.
www.mvcsoft.com
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();
}
}
68
MVCSoft Inc.
www.mvcsoft.com
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.
69
MVCSoft Inc.
www.mvcsoft.com
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.
70
MVCSoft Inc.
www.mvcsoft.com
}
We can pass the parameters as an Object[]. The order is the same order as they are
declared.
import java.util.Collection;
public RelParentForQuery()
{
71
MVCSoft Inc.
www.mvcsoft.com
import java.util.Collection;
public RelChildForQuery()
{
}
72
MVCSoft Inc.
www.mvcsoft.com
this.simpleObjects = simpleObjects;
}
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOHelper;
import javax.jdo.JDOException;
import java.util.Properties;
import java.util.ArrayList;
73
MVCSoft Inc.
www.mvcsoft.com
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;
}
return factory.getPersistenceManager();
}
pm.currentTransaction().commit();
}
catch (JDOException exception)
{
exception.printStackTrace();
pm.currentTransaction().rollback();
74
MVCSoft Inc.
www.mvcsoft.com
}
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;
return properties;
}
return factory.getPersistenceManager();
}
75
MVCSoft Inc.
www.mvcsoft.com
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();
}
}
76
MVCSoft Inc.
www.mvcsoft.com
}
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
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
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;
return properties;
}
return factory.getPersistenceManager();
}
79
MVCSoft Inc.
www.mvcsoft.com
properties.setProperty("com.mvcsoft.jdo.FunctionMap",
"com/mvcsoft/jdosamples/queries/simple_where/UserDefinedFunctions.prope
rties");
pm = getPersistenceManager(properties);
pm.currentTransaction().begin();
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();
}
}
80
MVCSoft Inc.
www.mvcsoft.com
{
query.setFilter("theInt.sqrt() >= 2");
printResults("filterSqrt", query.execute());
}
Here is a filter using the length function we defined in the properties file.
81
MVCSoft Inc.
www.mvcsoft.com
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
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
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.declareImports(
"import com.mvcsoft.jdosamples.queries.hyperquery.*");
MVCSoftPersistenceManagerFactory mvcPMF =
(MVCSoftPersistenceManagerFactory) factory;
UserFaultGroup zebraFaultGroup =
mvcPMF.getFaultGroup(Zebra.class.getName(), "zebraFaultGroup");
PersistenceManager pm = factory.getPersistenceManager();
query.declareImports(
"import com.mvcsoft.jdosamples.queries.hyperquery.*");
MVCSoftPersistenceManagerFactory mvcPMF =
(MVCSoftPersistenceManagerFactory) factory;
UserFaultGroup zebraFaultGroup =
mvcPMF.getFaultGroup(Zebra.class.getName(), "zebraFaultGroup");
UserFaultGroup antelopeFaultGroup =
factory.getFaultGroup(Antelope.class.getName(),
"antelopeFaultGroup");
84
MVCSoft Inc.
www.mvcsoft.com
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");
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
85
MVCSoft Inc.
www.mvcsoft.com
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
87
MVCSoft Inc.
www.mvcsoft.com
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
89
MVCSoft Inc.
www.mvcsoft.com
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
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.
91
MVCSoft Inc.
www.mvcsoft.com
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
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.
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
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
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
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
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
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
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
♦ 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
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;
101
MVCSoft Inc.
www.mvcsoft.com
import com.mvcsoft.jdo.runtime.util.FieldFacade;
import com.mvcsoft.jdo.runtime.util.ClassFacade;
♦ 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
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
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
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
♦ 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
♦ 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
♦ 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
109
MVCSoft Inc.
www.mvcsoft.com
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.
110
MVCSoft Inc.
www.mvcsoft.com
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
112
MVCSoft Inc.
www.mvcsoft.com
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
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
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;
115
MVCSoft Inc.
www.mvcsoft.com
116
MVCSoft Inc.
www.mvcsoft.com
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
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
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
♦ 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
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
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.
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
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
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
import java.util.Collection;
public Customer()
{
}
125
MVCSoft Inc.
www.mvcsoft.com
{
this.name = name;
}
}
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
public Order()
{
}
public LineItem()
{
}
127
MVCSoft Inc.
www.mvcsoft.com
128
MVCSoft Inc.
www.mvcsoft.com
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;
return properties;
}
return factory.getPersistenceManager();
}
pm.currentTransaction().begin();
Customer customer = new Customer("Dan", "Schenectady", "AAA",
"7631G", null);
pm.makePersistent(customer);
pm.currentTransaction().commit();
129
MVCSoft Inc.
www.mvcsoft.com
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();
}
}
import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Arrays;
130
MVCSoft Inc.
www.mvcsoft.com
{
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;
}
return factory.getPersistenceManager();
}
pm.currentTransaction().begin();
131
MVCSoft Inc.
www.mvcsoft.com
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();
}
}
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;
132
MVCSoft Inc.
www.mvcsoft.com
return properties;
}
JDOHelper.getPersistenceManagerFactory(getStandardProperties());
pm = getStandardPersistenceManager(factory);
pm.currentTransaction().begin();
133
MVCSoft Inc.
www.mvcsoft.com
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();
}
}
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
import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Arrays;
return properties;
}
JDOHelper.getPersistenceManagerFactory(getStandardProperties());
pm = getStandardPersistenceManager(factory);
pm.currentTransaction().begin();
135
MVCSoft Inc.
www.mvcsoft.com
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();
}
}
136
MVCSoft Inc.
www.mvcsoft.com
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;
return properties;
}
JDOHelper.getPersistenceManagerFactory(getStandardProperties());
pm = getStandardPersistenceManager(factory);
pm.currentTransaction().begin();
137
MVCSoft Inc.
www.mvcsoft.com
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
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();
}
}
}
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;
139
MVCSoft Inc.
www.mvcsoft.com
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;
}
JDOHelper.getPersistenceManagerFactory(getStandardProperties());
pm = getStandardPersistenceManager(factory);
pm.currentTransaction().begin();
pm.currentTransaction().begin();
// create the fault group
MVCSoftPersistenceManagerFactory mvcsoftFactory =
(MVCSoftPersistenceManagerFactory) factory;
String custClassName = Customer.class.getName();
140
MVCSoft Inc.
www.mvcsoft.com
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
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.
142
MVCSoft Inc.
www.mvcsoft.com
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
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.
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
implement the following MVCSoft interface (which can be a simple wrapper over
another logging system):
package com.mvcsoft.jdo.runtime.user;
145
MVCSoft Inc.
www.mvcsoft.com
146
MVCSoft Inc.
www.mvcsoft.com
import javax.transaction.Transaction;
147
MVCSoft Inc.
www.mvcsoft.com
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
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
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
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
152
MVCSoft Inc.
www.mvcsoft.com
153
MVCSoft Inc.
www.mvcsoft.com
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
public ObjToLock()
{
}
155
MVCSoft Inc.
www.mvcsoft.com
{
this.val1 = val1;
}
import javax.jdo.*;
import java.util.Properties;
import java.util.Collection;
import java.util.Iterator;
"com.mvcsoft.jdo.runtime.framework.MVCJDOPersistenceManagerFactory");
properties.setProperty("com.mvcsoft.jdo.MetaDataClass",
"com.mvcsoft.jdosamples.transactions.optimistic.MyMetadata");
156
MVCSoft Inc.
www.mvcsoft.com
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
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
}
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;
"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
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");
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
{
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;
"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
PersistenceManagerFactory factory =
JDOHelper.getPersistenceManagerFactory(properties);
PersistenceManager pm = factory.getPersistenceManager();
Transaction transaction=null;
try
{
transaction = pm.currentTransaction();
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
{
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
import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import java.util.Properties;
"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;
164
MVCSoft Inc.
www.mvcsoft.com
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
}
}
finally
{
try
{
if (pm1.currentTransaction().isActive())
pm1.currentTransaction().rollback();
}
catch (Exception e)
{
// log this exception
}
pm1.close();
}
166
MVCSoft Inc.
www.mvcsoft.com
167
MVCSoft Inc.
www.mvcsoft.com
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
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
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
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
<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
<mvcsoftjdo/>
</jdodoclet>
173
MVCSoft Inc.
www.mvcsoft.com
♦ 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
♦ 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
176
MVCSoft Inc.
www.mvcsoft.com
177
MVCSoft Inc.
www.mvcsoft.com
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
♦ 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
(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
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 ObjToTest()
{
}
181
MVCSoft Inc.
www.mvcsoft.com
import javax.jdo.*;
import javax.transaction.Synchronization;
import javax.transaction.Status;
import java.util.Properties;
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;
}
return factory.getPersistenceManager();
}
182
MVCSoft Inc.
www.mvcsoft.com
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");
}
});
183
MVCSoft Inc.
www.mvcsoft.com
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
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 CallbackP()
{
}
185
MVCSoft Inc.
www.mvcsoft.com
{
setDisplayName(displayName);
this.child = child;
}
// callback methods
Here we initialize displayName from the persistent state.
186
MVCSoft Inc.
www.mvcsoft.com
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.
}
Child Class Used in Sample
This is the class referenced in the relationship.
package com.mvcsoft.jdosamples.callbacks;
public CallbackC()
{
}
187
MVCSoft Inc.
www.mvcsoft.com
return testValue;
}
import javax.jdo.*;
import java.util.Properties;
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;
}
return factory.getPersistenceManager();
}
188
MVCSoft Inc.
www.mvcsoft.com
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
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.
190
MVCSoft Inc.
www.mvcsoft.com
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
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
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
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
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
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
197
MVCSoft Inc.
www.mvcsoft.com
198
MVCSoft Inc.
www.mvcsoft.com
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
public RabbitObject()
{
}
200
MVCSoft Inc.
www.mvcsoft.com
}
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;
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
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;
202
MVCSoft Inc.
www.mvcsoft.com
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 );
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
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;
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
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);
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());
}
}
205
MVCSoft Inc.
www.mvcsoft.com
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
For relationships, only the keys will be stored as part of the parent object’s
cached state.
207
MVCSoft Inc.
www.mvcsoft.com
import javax.transaction.Transaction;
208
MVCSoft Inc.
www.mvcsoft.com
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;
209
MVCSoft Inc.
www.mvcsoft.com
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;
210
MVCSoft Inc.
www.mvcsoft.com
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;
211
MVCSoft Inc.
www.mvcsoft.com
212
MVCSoft Inc.
www.mvcsoft.com
acquiring locks)
213
MVCSoft Inc.
www.mvcsoft.com
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
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.
215
MVCSoft Inc.
www.mvcsoft.com
}
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.
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
217
MVCSoft Inc.
www.mvcsoft.com
218
MVCSoft Inc.
www.mvcsoft.com
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;
219
MVCSoft Inc.
www.mvcsoft.com
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;
220
MVCSoft Inc.
www.mvcsoft.com
{
return jndiName;
}
221
MVCSoft Inc.
www.mvcsoft.com
222
MVCSoft Inc.
www.mvcsoft.com
223
MVCSoft Inc.
www.mvcsoft.com
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
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.
225
MVCSoft Inc.
www.mvcsoft.com
226
MVCSoft Inc.
www.mvcsoft.com
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;
227
MVCSoft Inc.
www.mvcsoft.com
{
}
}
228