An application can be JDO-enabled via many routes depending on the development process of
the project in question. For example the project could use Eclipse as the IDE for
developing classes. In that case the project would typically use the DataNucleus Eclipse
plugin. Alternatively the project could use Ant, Maven or some other build tool. In this
case this tutorial should be used as a guiding way for using DataNucleus in the
application. The JDO process is quite straightforward.
-
Step 0 : Download DataNucleus AccessPlatform
-
Step 1 : Design your domain/model classes as you would do normally
-
Step 2 : Define their persistence definition using Meta-Data.
-
Step 3 : Compile your classes, and instrument them (using the
DataNucleus enhancer).
-
Step 4 : Generate the database tables where your classes are to
be persisted.
-
Step 5 : Write your code to persist your objects within the DAO
layer.
-
Step 6 : Run your application.
-
Step 7 : Things to add.
The tutorial guides you through this. You can obtain the code referenced in this tutorial from
SourceForge (one of the files entitled "datanucleus-samples-tutorial-*").
You can download DataNucleus in many ways, but the simplest is to download the distribution
zip appropriate to your datastore. You can do this from
SourceForge DataNucleus download page.
When you open the zip you will find DataNucleus jars in the
lib
directory, and
dependency jars in the
deps
directory.
Do this as you would normally.
The only JDO constraint on any Java class that needs
persisting is that it has a default constructor (this can be private if you prefer, and
will actually be added by the DataNucleus Enhancer if you don't add it ;-) )
.
To give a working example, let us consider an application handling products in a store.
package org.datanucleus.samples.jdo.tutorial;
public class Product
{
String name = null;
String description = null;
double price = 0.0;
protected Product()
{
}
public Product(String name, String desc, double price)
{
this.name = name;
this.description = desc;
this.price = price;
}
}
package org.datanucleus.samples.jdo.tutorial;
public class Book extends Product
{
String author=null;
String isbn=null;
String publisher=null;
public Book(String name, String desc, double price, String author,
String isbn, String publisher)
{
super(name,desc,price);
this.author = author;
this.isbn = isbn;
this.publisher = publisher;
}
}
So we have inheritance between 2 classes. Some data in the store will be of type
Product
, and some will be
Book
. This allows us to extend our store further
in the future and provide
DVD
items for example, and so on. In traditional
persistence using, for example EJB CMP, this cannot be persisted directly. Instead the
developer would have to generate a level above the entity beans to define the inheritance.
This is messy. With JDO, we don't have this problem - we can simply throw objects across
to JDO and it will persist them, and allow them to be retrieved maintaining their
inheritances.
You now need to define how the classes should be persisted, in terms of which fields are
persisted etc. With JDO you could use
-
XML Metadata
-
Annotations
-
Annotations + XML
-
MetaData API at runtime
Here we use what could be considered a best practice, specifying basic persistence info
as annotations, and then adding on ORM information in XML (since if we want then to persist
to a different datastore later we don't need to update/recompile our classes, just change
the XML file). So for our 2 domain classes
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Product
{
String name = null;
String description = null;
double price = 0.0;
...
}
package org.datanucleus.samples.jdo.tutorial;
@PersistenceCapable
public class Book extends Product
{
String author=null;
String isbn=null;
String publisher=null;
...
}
and then we define ORM information in an XML file
<?xml version="1.0"?>
<!DOCTYPE orm PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/orm_2_0.dtd">
<orm>
<package name="org.datanucleus.samples.jdo.tutorial">
<class name="Product" identity-type="datastore">
<inheritance strategy="new-table"/>
<field name="name">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="description">
<column length="255" jdbc-type="VARCHAR"/>
</field>
</class>
<class name="Book" identity-type="datastore">
<inheritance strategy="new-table"/>
<field name="isbn">
<column length="20" jdbc-type="VARCHAR"/>
</field>
<field name="author">
<column length="40" jdbc-type="VARCHAR"/>
</field>
<field name="publisher">
<column length="40" jdbc-type="VARCHAR"/>
</field>
</class>
</package>
</orm>
With JDO you have various options as far as where this XML MetaData files is placed in
the file structure, and whether they refer to a single class, or multiple classes in a
package. With the above example, we have both classes specified in the same file
package-hsql.orm
, in the package these classes are in, since we want to persist
to HSQL.
In this tutorial we are using
datastore identity
which means that all objects of
these classes will be assigned an
identity
by DataNucleus to be able to reference
them. You should read about
datastore identity and
application identity when designing your systems persistence.
JDO relies on the classes that you want to persist being
PersistenceCapable
.
That is, they need to implement this Java interface. You could write your classes
manually to do this but this would be laborious. Alternatively you can use a
post-processing step to compilation that "enhances" your compiled classes, adding on the
necessary extra methods to make them
PersistenceCapable
. There are several ways
to do this, using an "enhancer" at compile time (with JDK1.6+), or at runtime, or
as a post-compile step. We use the post-compile step in this tutorial.
DataNucleus JDO
provides its own byte-code enhancer for instrumenting/enhancing
your classes for use by any JDO implementation. You will need to obtain the
datanucleus-enhancer
JAR for this.
To understand on how to invoke the enhancer you need to visualise where the various
source and jdo files are stored
src/java/org/datanucleus/samples/jdo/tutorial/Book.java
src/java/org/datanucleus/samples/jdo/tutorial/Product.java
src/java/org/datanucleus/samples/jdo/tutorial/package-hsql.orm
target/classes/org/datanucleus/samples/jdo/tutorial/Book.class
target/classes/org/datanucleus/samples/jdo/tutorial/Product.class
target/classes/org/datanucleus/samples/jdo/tutorial/package-hsql.orm
lib/jdo-api.jar
lib/datanucleus-core.jar
lib/datanucleus-enhancer.jar
lib/asm.jar
The first thing to do is compile your domain/model classes. You can do this in any way
you wish, but the downloadable JAR provides an Ant task, and a Maven2 project to do this
for you.
Using Ant :
ant compile
Using Maven2 :
mvn compile
To enhance classes using the DataNucleus Enhancer, you need to invoke a command something
like this from the root of your project.
Using Ant :
ant enhance
Using Maven : (this is usually done automatically after the "compile" goal)
mvn datanucleus:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-enhancer.jar:lib/datanucleus-core.jar:
lib/jdo-api.jar:lib/asm.jar
org.datanucleus.enhancer.DataNucleusEnhancer
target/classes/org/datanucleus/samples/jdo/tutorial/package-hsql.orm
Manually on Windows :
java -cp target\classes;lib\datanucleus-enhancer.jar;lib\datanucleus-core.jar;
lib\jdo-api.jar;lib\asm.jar
org.datanucleus.enhancer.DataNucleusEnhancer
target\classes\org\datanucleus\samples\jdo\tutorial\package-hsql.orm
[Command shown on many lines to aid reading - should be on single line]
This command enhances the .class files that have @PersistenceCapable annotations.
If you accidentally omitted this step, at the point of running your application and
trying to persist an object, you would get a
ClassNotPersistenceCapableException
thrown. The use of the enhancer is documented in more detail in the
Enhancer Guide. The output of this step are a set of
class files that represent
PersistenceCapable
classes.
This step is optional, depending on whether you have an existing database schema.
If you haven't, at this point you can use the
SchemaTool to generate the tables where these
domain objects will be persisted. DataNucleus SchemaTool is a command line utility (it
can be invoked from Maven2/Ant in a similar way to how the Enhancer is invoked). The
first thing that you need is to update the
datanucleus.properties
file with your
database details. Here we have a sample file (for HSQL)
javax.jdo.PersistenceManagerFactoryClass=org.datanucleus.jdo.JDOPersistenceManagerFactory
javax.jdo.option.ConnectionDriverName=org.hsqldb.jdbcDriver
javax.jdo.option.ConnectionURL=jdbc:hsqldb:mem:nucleus1
javax.jdo.option.ConnectionUserName=sa
javax.jdo.option.ConnectionPassword=
datanucleus.autoCreateSchema=true
datanucleus.validateTables=false
datanucleus.validateConstraints=false
Now we need to run DataNucleus SchemaTool. For our case above you would do
something like this
Using Ant :
ant createschema
Using Maven2 :
mvn datanucleus:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/datanucleus-core.jar:lib/datanucleus-rdbms.jar:
lib/jdo-api.jar:lib/{jdbc_driver.jar}
org.datanucleus.store.rdbms.SchemaTool
-props datanucleus.properties
-create
target/classes/org/datanucleus/samples/jdo/tutorial/package-hsql.orm
Manually on Windows :
java -cp target\classes;lib\datanucleus-core.jar;lib\datanucleus-rdbms.jar;
lib\jdo-api.jar;lib\{jdbc_driver.jar}
org.datanucleus.store.rdbms.SchemaTool
-props datanucleus.properties
-create
target\classes\org\datanucleus\samples\jdo\tutorial\package-hsql.orm
[Command shown on many lines to aid reading. Should be on single line]
This will generate the required tables, indexes, and foreign keys for the classes
defined in the JDO Meta-Data file.
Writing your own classes to be persisted is the start point, but you now need to define
which objects of these classes are actually persisted, and when. Interaction with the
persistence framework of JDO is performed via a PersistenceManager. This provides methods
for persisting of objects, removal of objects, querying for persisted objects, etc.
This section gives examples of typical scenarios encountered in an application.
The initial step is to obtain access to a PersistenceManager, which you do as follows
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("datanucleus.properties");
PersistenceManager pm = pmf.getPersistenceManager();
So we are creating a PersistenceManagerFactory using the file
datanucleus.properties
as used above for DataNucleus SchemaTool. This will contain all properties necessary for
our persistence usage. This file is found at the root of the CLASSPATH.
Now that the application has a PersistenceManager it can persist objects.
This is performed as follows
Transaction tx=pm.currentTransaction();
try
{
tx.begin();
Product product = new Product("Sony Discman","A standard discman from Sony",49.99);
pm.makePersistent(product);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
Please note that the
finally
step is important in that it tidies up connections to
the datastore and the PersistenceManager.
If you want to retrieve an object from persistent storage, something like this will give
what you need. This uses a "Query", and retrieves all Product objects that have a price
below 150.00, ordering them in ascending price order.
Transaction tx=pm.currentTransaction();
try
{
tx.begin();
Extent e = pm.getExtent(org.datanucleus.jdo.tutorial.Product.class,true);
Query q = pm.newQuery(e,"price < 150.00");
q.setOrdering("price ascending");
Collection c = (Collection)q.execute();
Iterator iter = c.iterator();
while (iter.hasNext())
{
Product p = (Product)iter.next();
... (use the retrieved objects)
}
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
If you want to delete an object from persistence, you would perform an operation
something like
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
... (retrieval of objects etc)
pm.deletePersistent(product);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
pm.close();
}
Clearly you can perform a large range of operations on objects.
We can't hope to show all of these here. Any good JDO book will provide many examples.
To run your JDO-enabled application will require a few things to be available in the
Java CLASSPATH, these being
-
Any properties file for the PersistenceManagerFactory creation
-
The JDO MetaData files for your persistable classes
-
Any JDBC driver classes needed for accessing your datastore
-
The JDO API JAR (defining the JDO interface)
-
The
DataNucleus Core
and
DataNucleus RDBMS
JARs
After that it is simply a question of starting your application and all should be taken care
of. You can access the DataNucleus Log file by specifying the
logging
configuration properties, and any messages from DataNucleus will be output in the normal way.
The DataNucleus log is a very powerful way of finding problems since it can list all SQL
actually sent to the datastore as well as many other parts of the persistence process.
Using Ant (you need the included "datanucleus.properties" to specify your database)
ant run
Using Maven2:
maven exec:java
Manually on Linux/Unix :
java -cp lib/jdo-api.jar:lib/datanucleus-core.jar:lib/datanucleus-rdbms.jar:
lib/hsql.jar:target/classes/:. org.datanucleus.samples.jdo.tutorial.Main
Manually on Windows :
java -cp lib\jdo-api.jar;lib\datanucleus-core.jar;lib\datanucleus-rdbms.jar;
lib\hsql.jar;target\classes\;. org.datanucleus.samples.jdo.tutorial.Main
Output :
DataNucleus Tutorial
=============
Persisting products
Product and Book have been persisted
Retrieving Extent for Products
> Product : Sony Discman [A standard discman from Sony]
> Book : JRR Tolkien - Lord of the Rings by Tolkien
Executing Query for Products with price below 150.00
> Book : JRR Tolkien - Lord of the Rings by Tolkien
Deleting all products from persistence
Deleted 2 products
End of Tutorial
Now that you have your simple tutorial working, you can look to adding on other features.
For example.
-
Add
datanucleus-connectionpool
to the CLASSPATH as well as the jars for DBCP,
or C3P0 or Proxool. This will improve performance significantly.
-
Add
datanucleus-cache
to the CLASSPATH as well as something like EHCache and you
get more scalable L2 caching
If you have any questions about this tutorial and how to develop applications for use
with
DataNucleus
please read the online documentation since answers are to be
found there. If you don't find what you're looking for go to our
Forums
.
Again, you can download the sample classes from this tutorial from
SourceForge.
The DataNucleus Team