An application can be JPA-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 Dali Eclipse plugin coupled with the JPOX 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 JPOX in the application. The JPA process is quite straightforward.
The tutorial guides you through this. You can obtain the code referenced in this tutorial from SourceForge (one of the files entitled "jpox-samples-tutorial-*").
Do this as you would normally. JPA places some constraints on persistable classes.
To give a working example, let us consider an application handling products in a store.
package org.jpox.samples.jpa.tutorial;
public class Product
{
long id;
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.jpox.samples.jpa.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 JPA, we don't have this problem - we can simply throw objects across to JPA 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 JPA you can define what classes are persistable with either MetaData (XML) or Java5 annotations. In this example we will use a mixture of both to highlight best practice. Let's start with which classes/fields are to be persisted. Here we use annotations. For example, for our 2 domain classes
package org.jpox.samples.jpa.tutorial;
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Product
{
@Id
long id;
@Basic
String name = null;
@Basic
String description = null;
@Basic
double price = 0.0;
...
}
package org.jpox.samples.jpa.tutorial;
@Entity
public class Book extends Product
{
@Basic
String author=null;
@Basic
String isbn=null;
@Basic
String publisher=null;
...
}So we have annotated both classes as "Entity" meaning that they are persistable. We have annotated the fields to be persisted, and the "id" field as being to store the identity. In addition we have defined that the classes will have one table in the datastore for each class. Now lets get on to the details of how they should be represented in an RDBMS datastore. We are going to define this in a MetaData file since it is better to keep schema information separate from persistence information. So we define a file orm.xml
<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
version="1.0">
<description>JPOX JPA tutorial</description>
<package>org.jpox.samples.jpa.tutorial</package>
<entity class="org.jpox.samples.jpa.tutorial.Product" name="Product">
<table name="JPA_PRODUCTS"/>
<attributes>
<id name="id">
<generated-value strategy="TABLE"/>
</id>
<basic name="name">
<column name="PRODUCT_NAME" length="100"/>
</basic>
<basic name="description">
<column length="255"/>
</basic>
</attributes>
</entity>
<entity class="org.jpox.samples.jpa.tutorial.Book" name="Book">
<table name="JPA_BOOKS"/>
<attributes>
<basic name="isbn">
<column name="ISBN" length="20"></column>
</basic>
<basic name="author">
<column name="AUTHOR" length="40"/>
</basic>
<basic name="publisher">
<column name="PUBLISHER" length="40"/>
</basic>
</attributes>
</entity>
</entity-mappings>We now need to tell JPA which classes we are going to persist. We do this via a file persistence.xml stored under "META-INF/" at the root of our package structure.
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<!-- JPA tutorial "unit" -->
<persistence-unit name="Tutorial">
<mapping-file>org/jpox/samples/jpa/tutorial/orm.xml</mapping-file>
<class>org.jpox.samples.metadata.store.Product</class>
<class>org.jpox.samples.metadata.store.Book</class>
</persistence-unit>
</persistence>So we have defined a persistence-unit called "Tutorial". This packages our classes to be persisted and we use that name later.
JPOX 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. JPOX provides a byte-code enhancer for instrumenting/enhancing your classes. You will need to obtain the JPOX Enhancer JAR to use this. To understand on how to invoke the enhancer you need to visualise where the various source and metadata files are stored src/java/META-INF/persistence.xml src/java/org/jpox/samples/jpa/tutorial/orm.xml src/java/org/jpox/samples/jpa/tutorial/Book.java src/java/org/jpox/samples/jpa/tutorial/Product.java target/classes/META-INF/persistence.xml target/classes/org/jpox/samples/jpa/tutorial/orm.xml target/classes/org/jpox/samples/jpa/tutorial/Book.class target/classes/org/jpox/samples/jpa/tutorial/Product.class lib/persistence-api.jar lib/jdo2-api.jar lib/jpox-core.jar lib/jpox-enhancer.jar lib/jpox-rdbms.jar lib/jpox-java5.jar lib/log4j.jar lib/asm.jar So you see that we have persistence-api.jar which is "JPA", jdo2-api.jar which is "JDO" (and JPOXs JPA support is built on top of JDO), jpox-java5.jar which contains JPOXs support for JPA and Java5, and in addition dependent jars. 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 Maven project to do this for you. Using Ant : ant compile Using Maven : maven java:compile To enhance classes using the JPOX 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 java:compile goal)
maven jpox:enhance
Manually on Linux/Unix :
java -cp target/classes:lib/jpox-enhancer.jar:lib/jpox-java5.jar:lib/jdo2-api.jar:
lib/persistence-api.jar:lib/jpox-core.jar:lib/log4j.jar:lib/asm.jar
-Dlog4j.configuration=file:log4j.properties
org.jpox.enhancer.JPOXEnhancer
-api JPA
-persistenceUnit Tutorial
Manually on Windows :
java -cp target\classes;lib\jpox-enhancer.jar;lib\jpox-java5.jar;lib\jdo2-api.jar;
lib\persistence-api.jar;lib\jpox-core.jar;lib\log4j.jar;lib\asm.jar
-Dlog4j.configuration=file:log4j.properties
org.jpox.enhancer.JPOXEnhancer
-api JPA
-persistenceUnit Tutorial
[Command shown on many lines to aid reading - should be on single line]The log4j.properties is a configuration file for logging, using Log4J. Any errors encountered during enhancement will be listed in the generated log file. This command enhances the .class files that are defined by the persistence.xml file. 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. You can alternatively build your application using either Maven or Ant instead of the above manual method. The use of the enhancer with these 2 build systems are documented 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. JPOX SchemaTool is a command line utility (it can be invoked from Maven/Ant in a similar way to how the Enhancer is invoked). The first thing that you need is to update the src/java/META-INF/persistence.xml file with your database details. Here we have a sample file (for HSQLDB) that contains
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<!-- Tutorial "unit" -->
<persistence-unit name="Tutorial">
<mapping-file>org/jpox/samples/jpa/tutorial/orm.xml</mapping-file>
<class>org.jpox.samples.jpa.tutorial.Product</class>
<class>org.jpox.samples.jpa.tutorial.Book</class>
<properties>
<property name="javax.jdo.option.ConnectionDriverName" value="org.hsqldb.jdbcDriver"/>
<property name="javax.jdo.option.ConnectionURL" value="jdbc:hsqldb:mem:jpox"/>
<property name="javax.jdo.option.ConnectionUserName" value="sa"/>
<property name="javax.jdo.option.ConnectionPassword" value=""/>
<property name="org.jpox.autoCreateSchema" value="true"/>
<property name="org.jpox.validateTables" value="false"/>
<property name="org.jpox.validateConstraints" value="false"/>
</properties>
</persistence-unit>
</persistence>Now we need to run JPOX SchemaTool. For our case above you would do something like this
Using Ant :
ant createschema
Using Maven :
maven jpox:schema-create
Manually on Linux/Unix :
java -cp target/classes:lib/jdo2-api.jar:lib/persistence-api.jar:lib/jpox-core.jar:
lib/jpox-rdbms.jar:lib/jpox-java5.jar:lib/log4j.jar:lib/{jdbc_driver.jar}
-Dlog4j.configuration=file:log4j.properties
org.jpox.SchemaTool
-create
-api JPA
-persistenceUnit Tutorial
Manually on Windows :
java -cp target\classes;lib\jdo2-api.jar;lib\persistence-api.jar;lib\jpox-core.jar;
lib\jpox-rdbms.jar;lib\jpox-java5.jar;lib\log4j.jar;lib\{jdbc_driver.jar}
-Dlog4j.configuration=file:log4j.properties
org.jpox.SchemaTool
-create
-api JPA
-persistenceUnit Tutorial
[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 annotations and orm.xml 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 JPA is performed via an EntityManager 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 an EntityManager, which you do as follows
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Tutorial");
EntityManager em = emf.createEntityManager();So we created an EntityManagerFactory for our "persistence-unit" called "Tutorial" (defined in persistence.xml earlier). Now that the application has an EntityManager it can persist objects. This is performed as follows
Transaction tx = em.getTransaction();
try
{
tx.begin();
Product product = new Product("Sony Discman", "A standard discman from Sony", 49.99);
em.persist(product);
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}Please note that the finally step is important in that it tidies up connections to the datastore and the EntityManager. Now we want to retrieve some objects from persistent storage, so we will use a "Query". In our case we want access to all Product objects that have a price below 150.00 and ordering them in ascending order.
Transaction tx = em.getTransaction();
try
{
tx.begin();
Query q = pm.createQuery("SELECT p FROM Product p WHERE p.price < 150.00");
List results = q.getResultList();
Iterator iter = results.iterator();
while (iter.hasNext())
{
Product p = (Product)iter.next();
... (use the retrieved object)
}
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}If you want to delete an object from persistence, you would perform an operation something like
Transaction tx = em.getTransaction();
try
{
tx.begin();
// Find and delete all objects whose last name is 'Jones'
Query q = em.createQuery("DELETE FROM Person p WHERE p.lastName = 'Jones'");
int numberInstancesDeleted = q.executeUpdate();
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
em.close();
}Clearly you can perform a large range of operations on objects. We can't hope to show all of these here. Any good JPA book will provide many examples.
To run your JPA-enabled application will require a few things to be available in the Java CLASSPATH, these being
After that it is simply a question of starting your application and all should be taken care of. You can access the JPOX JDO Log file by specifying the Log4J configuration properties, and any messages from JPOX will be output in the normal way. The JPOX 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 jpox.properties to specify your database)
ant run
Using Maven ("runtutorial" goal included in the download JAR):
maven runtutorial
Manually on Linux/Unix :
java -cp lib/persistence-api.jar:lib/jdo2-api.jar:lib/jpox-core.jar:lib/jpox-rdbms.jar:
lib/jpox-java5.jar:lib/log4j.jar:lib/mysql-connector-java.jar:target/classes/:.
org.jpox.samples.jpa.tutorial.Main
Manually on Windows :
java -cp lib\persistence-api.jar;lib\jdo2-api.jar;lib\jpox-core.jar;lib\jpox-rdbms.jar;
lib\jpox-java5.jar;lib\log4j.jar;lib\mysql-connector-java.jar;target\classes\;.
org.jpox.samples.jpa.tutorial.Main
Output :
JPOX Tutorial with JPA
======================
Persisting products
Product and Book have been persisted
Executing Query for Products with price below 150.00
> Book : JRR Tolkien - Lord of the Rings by Tolkien
Deleting all products from persistence
End of Tutorial
If you have any questions about this tutorial and how to develop applications for use with JPOX 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 JPOX Team |