blob: 6ee8ac293592cf4a752d891ec21a84360e5ce114 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<chapter id="jpa_tutorials">
<title>
JPA Tutorials
</title>
<section id="jpa_tutorials_intro">
<title>
OpenJPA Tutorials
</title>
<para>
These tutorials provide step-by-step examples of how to use various facets of
the OpenJPA system. They assume a general knowledge of JPA and Java. For more
information on these subjects, see the following URLs:
</para>
<itemizedlist>
<listitem>
<para>
<ulink url="http://java.sun.com/">Sun's Java site</ulink>
</para>
</listitem>
<listitem>
<para>
<link linkend="jpa_overview_intro">JPA Overview Document</link>
</para>
</listitem>
<listitem>
<para>
<link linkend="jpa_resources">Links to JPA</link>
</para>
</listitem>
</itemizedlist>
<section id="jpa_tutorial_requirements">
<title>
Tutorial Requirements
</title>
<para>
These tutorials require that JDK 1.5 or greater be installed on your computer,
and that <literal>java</literal> and <literal>javac</literal> are in your
<literal>PATH</literal> when you open a command shell.
</para>
</section>
</section>
<section id="jpa_tutorial">
<title>
OpenJPA Tutorial
</title>
<para>
In this tutorial you will become familiar with the basic tools and development
processes under OpenJPA by creating a simple JPA application.
</para>
<section id="jpa_tutorial_chapter1">
<title>
The Pet Shop
</title>
<para>
Imagine that you have decided to create a software toolkit to be used by pet
shop operators. This toolkit must provide a number of solutions to common
problems encountered at pet shops. Industry analysts indicate that the three
most desired features are inventory maintenance, inventory growth simulation,
and behavioral analysis. Not one to question the sage advice of experts, you
choose to attack these three problems first.
</para>
<para>
According to the aforementioned experts, most pet shops focus on three types of
animals only: dogs, rabbits, and snakes. This ontology suggests the following
class hierarchy:
</para>
<para>
<screen> Animal ^ | +--------------------+ | | | Dog Rabbit Snake</screen>
</para>
<section id="jpa_tutorial_files">
<title>
Included Files
</title>
<para>
We have provided an implementation of <classname>Animal</classname> and
<classname>Dog</classname> classes, plus some helper classes and files to create
the initial schema and populate the database with some sample dogs. Let's take a
closer look at these classes.
</para>
<itemizedlist>
<listitem>
<para>
<ulink url="../../../tutorial/persistence/AnimalMaintenance.java"><classname>
tutorial.persistence.AnimalMaintenance</classname></ulink>: Provides some
utility methods for examining and manipulating the animals stored in the
database. We will fill in method definitions in
<xref linkend="jpa_tutorial_chapter3"/>.
</para>
</listitem>
<listitem>
<para>
<ulink url="../../../tutorial/persistence/Animal.java"><classname>
tutorial.persistence.Animal</classname></ulink>: This is the superclass of all
animals that this pet store software can handle.
</para>
</listitem>
<listitem>
<para>
<ulink url="../../../tutorial/persistence/Dog.java"><classname>
tutorial.persistence.Dog</classname></ulink>: Contains data and methods
specific to dogs.
</para>
</listitem>
<listitem>
<para>
<ulink url="../../../tutorial/persistence/Rabbit.java"><classname>
tutorial.persistence.Rabbit</classname></ulink>: Contains data and methods
specific to rabbits. It will be used in <xref linkend="jpa_tutorial_chapter4"/>
.
</para>
</listitem>
<listitem>
<para>
<ulink url="../../../tutorial/persistence/Snake.java"><classname>
tutorial.persistence.Snake</classname></ulink>: Contains data and methods
specific to snakes. It will be used in <xref linkend="jpa_tutorial_chapter5"/>
.
</para>
</listitem>
<listitem>
<para>
<ulink url="../../../META-INF/persistence.xml"><filename>
../../META-INF/persistence.xml</filename></ulink>: This XML file contains
OpenJPA-specific and standard JPA configuration settings.
</para>
<para>
It is important to load all persistent entity classes at startup so that OpenJPA
can match database discriminator values to entity classes. Often this happens
automatically. Some parts of this tutorial, however, do require that all entity
classes be loaded explicitly. The JPA standard includes persistent class
listings in its XML configuration format. Add the following lines to <filename>
../../META-INF/persistence.xml</filename> between the <literal>&lt;provider&gt;
</literal> and the <literal>&lt;properties&gt;</literal> elements:
</para>
<programlisting>
&lt;class&gt;tutorial.persistence.Animal&lt;/class&gt;
&lt;class&gt;tutorial.persistence.Dog&lt;/class&gt;
</programlisting>
</listitem>
<listitem>
<para>
<ulink url="../../../tutorial/persistence/solutions"><filename>solutions
</filename></ulink>: The solutions directory contains the complete solutions to
this tutorial, including finished versions of the <filename>.java</filename>
files listed above.
</para>
</listitem>
</itemizedlist>
</section>
<section id="jpa_tutorial_utilities">
<title>
Important Utilities
</title>
<itemizedlist>
<listitem>
<para>
<command>java</command>: Runs main methods in specified Java classes.
</para>
</listitem>
<listitem>
<para>
<command>javac</command>: Compiles <filename>.java</filename> files into
<filename>.class</filename> files that can be executed by <command>java
</command>.
</para>
</listitem>
<listitem>
<para>
<indexterm>
<primary>
org.apache.openjpa.enhance.PCEnhancer
</primary>
</indexterm>
<command>org.apache.openjpa.enhance.PCEnhancer</command>:
Runs the OpenJPA enhancer against the specified
classes. More information is available in <xref linkend="ref_guide_pc_enhance"/>
of the Reference Guide.
</para>
</listitem>
<listitem>
<para>
<indexterm>
<primary>
org.apache.openjpa.jdbc.meta.MappingTool
</primary>
</indexterm>
<command>org.apache.openjpa.jdbc.meta.MappingTool</command>:
A utility that can be used to create and
maintain the object-relational mappings and schema of all persistent classes in
a JDBC-compliant datastore. This functionality allows the underlying mappings
and schema to be easily kept up-to-date with the Java classes in the system. See
<xref linkend="ref_guide_mapping"/> of the Reference Guide for more
information.
</para>
</listitem>
</itemizedlist>
</section>
</section>
<section id="jpa_tutorial_chapter2">
<title>
Getting Started
</title>
<para>
Let's compile the initial classes and see them in action. To do so, we must
compile the <filename>.java</filename> files, as we would with any Java project,
and then optionally pass the resulting classes through the OpenJPA enhancer.
</para>
<note>
<para>
<indexterm>
<primary>
CLASSPATH
</primary>
</indexterm>
Be sure that your <envar>CLASSPATH</envar> is set correctly. Note that your
OpenJPA install directory should be in the <envar>CLASSPATH</envar>, as the
tutorial classes are located in the <literal> tutorial/persistence</literal>
directory under your OpenJPA install directory, and are in the <literal>
tutorial.persistence</literal> package.
</para>
</note>
<orderedlist>
<listitem>
<para>
Make sure you are in the <filename> tutorial/persistence</filename> directory.
All examples throughout the tutorial assume that you are in this directory.
</para>
</listitem>
<listitem>
<para>
Examine <filename>Animal.java</filename>, <filename>Dog.java</filename>, and
<filename>SeedDatabase.java</filename>
</para>
<para>
These files are good examples of the simplicity JPA engenders. As noted earlier,
persisting an object or manipulating an object's persistent data requires almost
no JPA-specific code. For a very simple example of creating persistent objects,
please see the <literal>seed</literal> method of <filename>SeedDatabase.java
</filename>. Note the objects are created with normal Java constructors. The
files <filename>Animal.java</filename> and <filename>Dog.java</filename> are
also good examples of how JPA allows you to manipulate persistent data without
writing any specific JPA code, by providing simple annotations.
</para>
<para>
Let's take a look at the <filename>Animal.java</filename> file. Notice that the
class is a Plain Old Java Object (POJO), with several annotations describing how
the class is mapped into a relational database. First, let's examine the class
level annotations:
</para>
<programlisting>
@Entity(name="Animal")
@Table(name="JPA_TUT_ANIMAL")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="SPECIES", length=100)
public abstract class Animal
{
...
}
</programlisting>
<para>
The annotations serve to map the class into the database. For more information
on these and other annotations, see <xref linkend="jpa_overview_meta"/>
and <xref linkend="jpa_overview_mapping"/>.
</para>
<orderedlist>
<listitem>
<para>
<command>@Entity</command>: This annotation indicates that instances of this
class may be persistent entities. The value of the <command>name</command>
attribute is the entity name, and is used in queries, etc.
</para>
</listitem>
<listitem>
<para>
<command>@Table</command>: This annotation is used to map the entity to a
primary table. The value of the <command>name</command> attribute specifies the
name of the relational table to use as the primary table.
</para>
</listitem>
<listitem>
<para>
<command>@Inheritance</command>: When multiple classes in an inheritance
hierarchy are persistent entity types, it is important to describe how the
inheritance hierarchy is mapped. Setting the value of the <command>strategy
</command> attribute to <command>InheritanceType.SINGLE_TABLE</command>
indicates that the primary table for all subclasses shall be the same table as
for the superclass.
</para>
</listitem>
<listitem>
<para>
<command>@DiscriminatorColumn</command>: With a <command>SINGLE_TABLE</command>
inheritance mapping strategy, instances of multiple classes will be stored in
the same table. This annotation describes a column in that table that is used to
determine the type of an instance whose data is stored in a particular row. The
<command>name</command> attribute is the name of the column, and the <command>
length</command> attribute indicates the size of the column. By default, the
unqualified class name for the instance is stored in the discriminator column.
To store a different value for a type, use the <command>@DiscriminatorValue
</command> annotation.
</para>
</listitem>
</orderedlist>
<para>
Let's take a look at our class' field annotations. We have chosen to use
<emphasis>field access</emphasis> for our entities, meaning the persistence
implementation will get and set persistent state directly through our class'
declared fields. We could have chosen to use <emphasis> property access
</emphasis>, in which the implementation accesses persistent state through our
JavaBean getter and setter methods. In that case, we would have annotated our
getter methods rather than our fields.
</para>
<programlisting>
@Id
@GeneratedValue
@Column(name="ID")
private long id;
@Basic @Column(name="ANIMAL_NAME")
private String name = null;
@Basic @Column(name="COST")
private float price = 0f;
</programlisting>
<para>
The annotations serve to map the fields into the database. For more information
on these and other annotations, see <xref linkend="jpa_overview_meta"/>.
</para>
<orderedlist>
<listitem>
<para>
<command>@Id</command>: This annotation indicates that the field is to be
mapped to a primary key column in the database.
</para>
</listitem>
<listitem>
<para>
<command>@GeneratedValue</command>: Indicates that the implementation will
generate a value for the field automatically.
</para>
</listitem>
<listitem>
<para>
<command>@Column</command>: This annotation describes the column to which the
field will be mapped. The <command>name</command> attribute specifies the name
of the column.
</para>
</listitem>
<listitem>
<para>
<command>@Basic</command>: This annotation indicates that the field is simply
mapped into a column. There are other annotations that indicate entity
relationships and other more complex mappings.
</para>
</listitem>
</orderedlist>
</listitem>
<listitem>
<para>
Compile the <filename>.java</filename> files.
</para>
<programlisting>
javac *.java
</programlisting>
<para>
You can use any Java compiler instead of <command>javac</command>.
</para>
</listitem>
<listitem>
<para>
Enhance the persistent classes. (Optional)
</para>
<programlisting>
java org.apache.openjpa.enhance.PCEnhancer Animal.java Dog.java
</programlisting>
<para>
This step runs the OpenJPA enhancer on the <filename>Animal.java</filename> and
<filename>Dog.java</filename> files mentioned above. See
<xref linkend="ref_guide_pc_enhance"/> of the Reference Guide for more
information on the enhancer, including alternatives to enhancement.
</para>
<note>
<para>
The <literal>-p</literal> flag points the enhancer to your <filename>
persistence.xml</filename> configuration file. All OpenJPA tools look for
default configuration in a resource called <filename>openjpa.xml</filename> or
<filename>META-INF/openjpa.xml</filename>. Thus you can avoid passing the
<literal>-p</literal> argument to tools by using this configuration file name in
place of <filename>persistence.xml</filename>. See
<xref linkend="ref_guide_conf"/> in the Reference Guide for details on
OpenJPA configuration.
</para>
</note>
</listitem>
</orderedlist>
<section id="jpa_tutorial_chapter2_datastore">
<title>
Configuring the Datastore
</title>
<para>
Now that we've compiled the source files,
we're ready to set up the database. <ulink url="http://hsqldb.sourceforge.net">
Hypersonic SQL</ulink>, a pure Java relational database, is included in the
OpenJPA distribution. We have included this database because it is simple to set
up and has a small memory footprint; however, you can use this tutorial with any
of the relational databases that we support. You can also write your own plugin
for any database that we do not support. For the sake of simplicity, this
tutorial only describes how to set up connectivity to a Hypersonic SQL database.
For more information on how to connect to a different database or how to add
support for other databases, see <xref linkend="ref_guide_dbsetup"/> of
the Reference Guide.
</para>
<orderedlist>
<listitem>
<para>
<indexterm>
<primary>
org.apache.openjpa.jdbc.meta.MappingTool
</primary>
</indexterm>
Create the object-relational mappings and database schema.
</para>
<programlisting>
java org.apache.openjpa.jdbc.meta.MappingTool Animal.java Dog.java
</programlisting>
<para>
This command propagates the necessary schema for the specified classes to the
database configured in <filename>persistence.xml</filename>. If you are using
the default Hypersonic SQL setup, the first time you run the mapping tool
Hypersonic will create <filename>tutorial_database.properties</filename> and
<filename>tutorial_database.script</filename> database files in your current
directory. To delete the database, just delete these files.
</para>
<para>
By default, JPA uses object-relational mapping information stored in annotations
in your source files. <xref linkend="jpa_overview_mapping"/> of the JPA
Overview will help you understand mapping annotations. Additionally,
<xref linkend="ref_guide_mapping"/> of the Reference Guide describes your
other mapping options in detail.
</para>
<para>
</para>
<para>
If you are curious, you can view the schema OpenJPA created for the tutorial
classes with OpenJPA's schema tool:
</para>
<programlisting>
java org.apache.openjpa.jdbc.schema.SchemaTool -a reflect -f tmp.schema
</programlisting>
<para>
This will create a <filename>tmp.schema</filename> file with an XML
representation of the database schema. The XML should be self explanatory; see
<xref linkend="ref_guide_schema_xml"/> of the Reference Guide for details.
You may delete the <filename>tmp.schema</filename> file before proceeding.
</para>
</listitem>
<listitem>
<para>
Populate the database with sample data.
</para>
<programlisting>
java tutorial.persistence.SeedDatabase
</programlisting>
</listitem>
</orderedlist>
<para>
Congratulations! You have now created an JPA-accessible persistent store, and
seeded it with some sample data.
</para>
</section>
</section>
<section id="jpa_tutorial_chapter3">
<title>
Inventory Maintenance
</title>
<para>
The most important element of a successful pet store product, say the experts,
is an inventory maintenance mechanism. So, let's work on the <classname>Animal
</classname> and <classname>Dog</classname> classes a bit to permit user
interaction with the database.
</para>
<para>
This chapter should familiarize you with some of the basics of the
<ulink url="../../ejb-3_0-pr-spec-persistence.pdf">JPA specification</ulink> and
the mechanics of compiling and enhancing persistent classes. You will also
become familiar with the mapping tool for propagating the persistent schema into
the database.
</para>
<para>
First, let's add some code to <filename>AnimalMaintenance.java</filename> that
allows us to examine the animals currently in the database.
</para>
<orderedlist>
<listitem>
<para>
Add code to <filename>AnimalMaintenance.java</filename>.
</para>
<para>
Modify the <methodname>getAnimals</methodname> method of <filename>
AnimalMaintenance.java</filename> to look like this:
</para>
<programlisting>
/**
* Return a list of animals that match the specified query filter.
*
* @param filter the JPQL filter to apply to the query
* @param cls the class of animal to query on
* @param em the EntityManager to obtain the query from
*/
public static List getAnimals(String filter, EntityManager em)
{
// Execute a query for the specified filter.
Query query = em.createQuery(filter);
return query.getResultList();
}
</programlisting>
</listitem>
<listitem>
<para>
Compile <filename>AnimalMaintenance.java</filename>.
</para>
<programlisting>
javac AnimalMaintenance.java
</programlisting>
</listitem>
<listitem>
<para>
Take a look at the animals in the database.
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance list Animal
</programlisting>
<para>
Notice that <methodname>list</methodname> optionally takes a query filter. Let's
explore the database some more, this time using filters:
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance list "select a from Animal a where a.name = 'Binney'"
java tutorial.persistence.AnimalMaintenance list "select a from Animal a where a.price &lt;= 50"
</programlisting>
<para>
The Java Persistence Query Language (JPQL) is designed to look and behave much
like an object oriented SQL dialect. The <literal>name</literal> and <literal>
price</literal> fields identified in the above queries map to the member fields
of those names in <classname> tutorial.persistence.Animal</classname>. More
details on JPQL syntax is available in <xref linkend="jpa_overview_query"/>
of the JPA Overview.
</para>
</listitem>
</orderedlist>
<para>
Great! Now that we can see the contents of the database, let's add some code
that lets us add and remove animals.
</para>
<section id="jpa_tutorial_chapter3_persist">
<title>
Persisting Objects
</title>
<para>
As new dogs are born or acquired, the store owner will need to add new records
to the inventory database. In this section, we'll write the code to handle
additions through the <classname>tutorial.persistence.AnimalMaintenance
</classname> class.
</para>
<para>
This section will familiarize you with the mechanism for storing persistent
instances in a JPA entity manager. We will create a new dog, obtain a
<classname>Transaction</classname> from a <classname>EntityManager</classname>,
and, within the transaction, make the new dog object persistent.
</para>
<para>
<classname>tutorial.persistence.AnimalMaintenance</classname> provides a
reflection-based facility for creating any type of animal, provided that the
animal has a two-argument constructor whose first argument corresponds to the
name of the animal and whose second argument is an implementation-specific
primitive. This reflection-based system is in place to keep this tutorial short
and remove repetitive creation mechanisms. It is not a required part of the JPA
specification.
</para>
<orderedlist>
<listitem>
<para>
Add the following code to <filename> AnimalMaintenance.java</filename>.
</para>
<para>
Modify the <methodname>persistObject</methodname> method of <filename>
AnimalMaintenance.java</filename> to look like this:
</para>
<programlisting>
/**
* Performs the actual JPA work of putting &lt;code&gt;object&lt;/code&gt;
* into the data store.
*
* @param object the object to persist in the data store
*/
public static void persistObject(EntityManager em, Object object)
{
// Mark the beginning of the unit of work boundary.
em.getTransaction().begin();
em.persist(object);
// Mark the end of the unit of work boundary,
// and record all inserts in the database.
em.getTransaction().commit();
System.out.println("Added " + object);
}
</programlisting>
<note>
<para>
In the above code, we pass in an <classname>EntityManager</classname>.
<classname>EntityManager</classname>s may be either container managed or
application managed. In this tutorial, because we're operating outside a
container, we're using application managed <classname>EntityManager</classname>
s. In managed environments, <classname>EntityManager</classname>s are typically
container managed, and thus injected or looked up via JNDI. Application managed
<classname>EntityManager</classname>s can be used in both managed and unmanaged
environments, and are created by an <classname>EntityManagerFactory</classname>
. An <classname>EntityManagerFactory</classname> can be obtained from the
<classname>javax.persistence.Persistence</classname> class. This class provides
some convenience methods for obtaining an <classname>EntityManagerFactory
</classname>.
</para>
</note>
</listitem>
<listitem>
<para>
Recompile <filename>AnimalMaintenance.java</filename>.
</para>
<programlisting>
javac AnimalMaintenance.java
</programlisting>
</listitem>
</orderedlist>
<para>
You now have a mechanism for adding new dogs to the database. Go ahead and add
some by running <command>java tutorial.persistence.AnimalMaintenance add Dog
&lt;name&gt; &lt;price&gt;</command> For example:
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance add Dog Fluffy 35
</programlisting>
<para>
You can view the contents of the database with:
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance list Dog
</programlisting>
</section>
<section id="jpa_tutorial_chapter3_delete">
<title>
Deleting Objects
</title>
<para>
What if someone decides to buy one of the dogs? The store owner will need to
remove that animal from the database, since it is no longer in the inventory.
</para>
<para>
This section demonstrates how to remove data from the datastore.
</para>
<orderedlist>
<listitem>
<para>
Add the following code to <filename>AnimalMaintenance.java</filename>.
</para>
<para>
Modify the <methodname>deleteObjects</methodname> method of <filename>
AnimalMaintenance.java</filename> to look like this:
</para>
<programlisting>
/**
* Performs the actual JPA work of removing
* &lt;code&gt;objects&lt;/code&gt; from the datastore.
*
* @param objects the objects to persist in the datastore
* @param em the EntityManager to delete with
*/
public static void deleteObjects(Collection objects, EntityManager em)
{
// Mark the beginning of the unit of work boundary.
em.getTransaction().begin();
// This method removes the objects in 'objects' from the data store.
for (Object ob : objects)
{
System.out.println("Removed animal: " + ob);
em.remove(ob);
}
// Mark the end of the unit of work boundary, and record all
// deletes in the database.
em.getTransaction().commit();
}
</programlisting>
</listitem>
<listitem>
<para>
Recompile <filename>AnimalMaintenance.java</filename>.
</para>
<programlisting>
javac AnimalMaintenance.java
</programlisting>
</listitem>
<listitem>
<para>
Remove some animals from the database.
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance remove &lt;query&gt;
</programlisting>
<para>
Where <replaceable>&lt;query&gt;</replaceable> is a query string like those used
for listing animals above.
</para>
</listitem>
</orderedlist>
<para>
All right. We now have a basic pet shop inventory management system. From this
base, we will add some of the more advanced features suggested by our industry
experts.
</para>
</section>
</section>
<section id="jpa_tutorial_chapter4">
<title>
Inventory Growth
</title>
<para>
Now that we have the basic pet store framework in place, let's add support for
the next pet in our list: the rabbit. The rabbit is a bit different than the
dog; pet stores sell them all for the same price, but gender is critically
important since rabbits reproduce rather easily and quickly. Let's put together
a class representing a rabbit.
</para>
<para>
In this chapter, you will see some more queries and write a bidirectional
relation between objects.
</para>
<para>
Provided with this tutorial is a file called <filename>Rabbit.java</filename>
which contains a sample <classname>Rabbit</classname> implementation.
</para>
<orderedlist>
<listitem>
<para>
Examine <filename>Rabbit.java</filename>.
</para>
</listitem>
<listitem>
<para>
The Rabbit class above contains a bidirectional relationship between parents and
children. From the Java side of things, a bidirectional relationship is simply a
pair of fields that are conceptually linked. There is no special Java work
necessary to express bidirectionality. However, you must identify the
relationship as bidirectional using JPA <link linkend="jpa_overview_meta">
annotations</link> so the mapping tool can create the most efficient schema.
</para>
<para>
Insert this snippet of code immediately <emphasis>before</emphasis> the
<literal>children</literal> field declaration in the <filename>Rabbit.java
</filename> file.
</para>
<programlisting>
@ManyToMany
@JoinTable(name="RABBIT_CHILDREN",
joinColumns=@JoinColumn(name="PARENT_ID"),
inverseJoinColumns=@JoinColumn(name="CHILD_ID"))
</programlisting>
<para>
The <literal>@ManyToMany</literal> annotation indicates that <literal>children
</literal> is one side of a many-to-many relation. <literal>@JoinTable</literal>
describes how this relation maps to a database join table. The annotation's
<literal>joinColumns</literal> name the join table's foreign key columns linking
to the owning instance (the parent). In this case, column <literal>
RABBIT_CHILDREN.PARENT_ID</literal> is a foreign key to the parent's <literal>
ID</literal> primary key column. Similarly, the <literal>inverseJoinColumns
</literal> attribute denotes the foreign key columns linking to the collection
elements (the children). For more details on the <literal>@JoinTable</literal>
annotation, see <xref linkend="jpa_overview_mapping"/> of the JPA
Overview.
</para>
<para>
Now we'll map the other side of this bidirectional relation, the <literal>
parents</literal> field. Insert the following snippet of code immediately
<emphasis>before</emphasis> the <command>parents</command> field declaration in
the <filename>Rabbit.java</filename> file. The <literal>mappedBy</literal>
attribute identifies the name of the owning side of the relation.
</para>
<programlisting>
@ManyToMany(mappedBy="children")
</programlisting>
</listitem>
<listitem>
<para>
Compile <filename>Rabbit.java</filename>.
</para>
<programlisting>
javac Rabbit.java
</programlisting>
</listitem>
<listitem>
<para>
Enhance the <classname>Rabbit</classname> class. (Optional)
</para>
<programlisting>
java org.apache.openjpa.enhance.PCEnhancer Rabbit.java
</programlisting>
</listitem>
<listitem>
<para>
Refresh the object-relational mappings and database schema.
</para>
<programlisting>
java org.apache.openjpa.jdbc.meta.MappingTool Rabbit.java
</programlisting>
</listitem>
</orderedlist>
<para>
Now that we have a Rabbit class, let's get some preliminary rabbit data into the
database.
</para>
<orderedlist>
<listitem>
<para>
Add a <literal>&lt;class&gt;</literal> entry for <classname> Rabbit</classname>
to <filename>../../META-INF/persistence.xml</filename>.
</para>
<programlisting>
&lt;class&gt;tutorial.persistence.Rabbit&lt;/class&gt;
</programlisting>
</listitem>
<listitem>
<para>
Create some rabbits.
</para>
<para>
Run the following commands a few times to add some male and female rabbits to
the database:
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance add Rabbit &lt;name&gt; false
java tutorial.persistence.AnimalMaintenance add Rabbit &lt;name&gt; true
</programlisting>
<para>
Now run some breeding iterations.
</para>
<programlisting>
java tutorial.persistence.Rabbit breed 2
</programlisting>
</listitem>
<listitem>
<para>
Look at your new rabbits.
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance list Rabbit
java tutorial.persistence.AnimalMaintenance details "select r from Rabbit r where r.name = '&lt;name&gt;'"
</programlisting>
</listitem>
</orderedlist>
</section>
<section id="jpa_tutorial_chapter5">
<title>
Behavioral Analysis
</title>
<para>
Often, pet stores sell snakes as well as rabbits and dogs. Pet stores are
primarily concerned with a snake's length; much like rabbits, pet store
operators usually sell them all for a flat rate.
</para>
<para>
This chapter demonstrates more queries, schema manipulation, and additional
relation types.
</para>
<para>
Provided with this tutorial is a file called <filename>Snake.java</filename>
which contains a sample <classname>Snake</classname> implementation. Let's get
it compiled and loaded:
</para>
<orderedlist>
<listitem>
<para>
Examine and compile <filename>Snake.java</filename>.
</para>
<programlisting>
javac Snake.java
</programlisting>
</listitem>
<listitem>
<para>
Enhance the class. (Optional)
</para>
<programlisting>
java org.apache.openjpa.enhance.PCEnhancer Snake.java
</programlisting>
</listitem>
<listitem>
<para>
Refresh the mappings and database.
</para>
<para>
As we have created a new persistent class, we must map it to the database and
change the schema to match. So run the mapping tool:
</para>
<programlisting>
java org.apache.openjpa.jdbc.meta.MappingTool Snake.java
</programlisting>
</listitem>
<listitem>
<para>
Add a <literal>&lt;class&gt;</literal> entry for <classname> Snake</classname>
to <filename>../../META-INF/persistence.xml</filename>.
</para>
<programlisting>
&lt;class&gt;tutorial.persistence.Snake&lt;/class&gt;
</programlisting>
</listitem>
</orderedlist>
<para>
Once you have compiled everything, add a few snakes to the database using:
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance add Snake "name" &lt;length&gt;
</programlisting>
<para>
Where <replaceable>&lt;length&gt;</replaceable> is the length in feet for the
new snake. To see the new snakes in the database, run:
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance list Snake
</programlisting>
<para>
Unfortunately for the massively developing rabbit population, snakes often eat
rabbits. Any good inventory system should be able to capture this behavior. So,
let's add some code to <filename>Snake.java</filename> to support the snake's
eating behavior.
</para>
<para>
First, let's modify <filename>Snake.java</filename> to contain a list of eaten
rabbits.
</para>
<orderedlist>
<listitem>
<para>
Add the following code snippets to <filename>Snake.java</filename>.
</para>
<programlisting>
// This list will be persisted into the database as
// a one-to-many relation.
@OneToMany(mappedBy="eater")
private Set&lt;Rabbit&gt; giTract = new HashSet&lt;Rabbit&gt; ();
</programlisting>
<para>
Note that we specified a <literal>mappedBy</literal> attribute in this example.
This is because the relation is bidirectional; that is, the rabbit has knowledge
of which snake ate it. We could have left out the <literal>eater</literal> field
and instead created a standard unidirectional relation. In fact, in a
bidirectional many-to-one relation, the many side must always be the owner.
</para>
<para>
For more information on types of relations, see
<xref linkend="jpa_overview_mapping_field"/> of the JPA Overview.
</para>
<para>
Modify the <literal>toString(boolean)</literal> method to output the giTract
list.
</para>
<programlisting>
public String toString(boolean detailed)
{
StringBuffer buf = new StringBuffer(1024);
buf.append("Snake ").append(getName());
if (detailed)
{
buf.append(" (").append(length).append(" feet long) sells for ");
buf.append(getPrice()).append(" dollars.");
buf.append(" Its gastrointestinal tract contains:\n");
for (Rabbit rabbit : giTract)
buf.append("\t").append(rabbit).append("\n");
}
else
buf.append("; ate " + giTract.size() + " rabbits.");
return buf.toString();
}
</programlisting>
<para>
Add the following methods.
</para>
<programlisting>
/**
* Kills the specified rabbit and eats it.
*/
public void eat(Rabbit dinner)
{
// Consume the rabbit.
dinner.kill();
dinner.setEater(this);
giTract.add(dinner);
System.out.println("Snake " + getName() + " ate rabbit "
+ dinner.getName() + ".");
}
/**
* Locates the specified snake and tells it to eat a rabbit.
*/
public static void eat(EntityManager em, String filter)
{
em.getTransaction().begin();
// Find the desired snake(s) in the data store.
Query query = em.createQuery(filter);
List&lt;Snake&gt; results = query.getResultList();
if (results.isEmpty())
{
System.out.println("No snakes matching '" + filter + "' found");
return;
}
Query uneatenQuery = em.createQuery
("select r from Rabbit r where r.isDead = false");
Random random = new Random();
for (Snake snake : results)
{
// Run a query for a rabbit whose 'isDead' field indicates
// that it is alive.
List&lt;Rabbit&gt; menu = uneatenQuery.getResultList();
if (menu.isEmpty())
{
System.out.println("No live rabbits in DB.");
break;
}
// Select a random rabbit from the list.
Rabbit dinner = menu.get(random.nextInt(menu.size()));
// Perform the eating.
System.out.println(snake + " is eating:");
snake.eat(dinner);
}
em.getTransaction().commit();
}
public static void main(String[] args)
{
if (args.length == 2 &amp;&amp; args[0].equals("eat"))
{
EntityManagerFactory emf = Persistence.
createEntityManagerFactory(null);
EntityManager em = emf.createEntityManager();
eat(em, args[1]);
em.close();
emf.close();
return;
}
// If we get here, something went wrong.
System.out.println("Usage:");
System.out.println(" java tutorial.persistence.Snake eat "
+ "\"snakequery\"");
}
</programlisting>
</listitem>
<listitem>
<para>
Add an <literal>eater</literal> field to <filename>Rabbit.java</filename>, and
a getter and setter.
</para>
<programlisting>
@ManyToOne @JoinColumn(name="EATER_ID")
private Snake eater;
...
public Snake getEater()
{
return eater;
}
public void setEater(Snake snake)
{
eater = snake;
}
</programlisting>
<para>
The <literal>@ManyToOne</literal> annotation indicates that this is the many
side of the bidirectional relation. The many side must always be the owner in
this type of relation. The <literal>@JoinColumn</literal> describes the foreign
key that joins the rabbit table to the snake table. The rabbit table has an
<literal>EATER_ID</literal> column that is a foreign key to the <literal>ID
</literal> primary key column of the snake table.
</para>
</listitem>
<listitem>
<para>
Compile <filename>Snake.java</filename> and <filename>Rabbit.java</filename> and
optionally enhance the classes.
</para>
<programlisting>
javac Snake.java Rabbit.java
java org.apache.openjpa.enhance.PCEnhancer Snake.java Rabbit.java
</programlisting>
</listitem>
<listitem>
<para>
Refresh the mappings and database.
</para>
<programlisting>
java org.apache.openjpa.jdbc.meta.MappingTool Snake.java Rabbit.java
</programlisting>
</listitem>
</orderedlist>
<para>
Now, experiment with the following commands:
</para>
<programlisting>
java tutorial.persistence.Snake eat "select s from Snake s where s.name = '&lt;name&gt;'"
java tutorial.persistence.AnimalMaintenance details "select s from Snake s where s.name = '&lt;name&gt;'"
</programlisting>
<section id="jpa_tutorial_chapter5_query">
<title>
Complex Queries
</title>
<para>
Imagine that one of the snakes in the database was named Killer. To find out
which rabbits Killer ate, we could run either of the following two queries:
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance details "select s from Snake s where s.name = 'Killer'"
java tutorial.persistence.AnimalMaintenance list "select r from Rabbit r where r.eater.name = 'Killer'"
</programlisting>
<para>
The first query is snake-centric - the query runs against the <classname>Snake
</classname> class, looking for all snakes named Killer and providing a detailed
listing of them. The second is rabbit-centric - it examines the rabbits in the
database for instances whose <literal>eater</literal> is named Killer. This
second query demonstrates that the simple Java 'dot' syntax is used when
traversing a to-one field in a query.
</para>
<para>
It is also possible to traverse collection fields. Imagine that there was a
rabbit called Roger in the datastore and that one of the snakes ate it. In order
to determine who ate Roger Rabbit, you could run a query like this:
</para>
<programlisting>
java tutorial.persistence.AnimalMaintenance details "select s from Snake s inner join s.giTract r where r.name = 'Roger'"
</programlisting>
</section>
</section>
<section id="jpa_tutorial-chapter6">
<title>
Extra Features
</title>
<para>
Congratulations! You are now the proud author of a pet store inventory suite.
Now that you have all the major features of the pet store software implemented,
it's time to add some extra features. You're on your own; think of some features
that you think a pet store should have, or just explore the features of JPA.
</para>
<para>
Here are a couple of suggestions to get you started:
</para>
<itemizedlist>
<listitem>
<para>
Animal pricing.
</para>
<para>
Modify <classname>Animal</classname> to contain an inventory cost and a resale
price. Calculate the real dollar amount eaten by the snakes (the sum of the
inventory costs of all the consumed rabbits), and the cost assuming that all the
eaten rabbits would have been sold had they been alive. Ignore the fact that the
rabbits, had they lived, would have created more rabbits, and the implications
of the reduced food costs due to the not-quite-as-hungry snakes and the smaller
number of rabbits.
</para>
</listitem>
<listitem>
<para>
Dog categorization.
</para>
<para>
Modify <classname>Dog</classname> to have a relation to a new class called
<classname>Breed</classname>, which contains a name identifying the breed of
the dog and a description of the breed. Put together an admin tool for breeds
and for associating dogs and breeds.
</para>
</listitem>
</itemizedlist>
</section>
</section>
<section id="jpa_j2ee_tutorial">
<title>
J2EE Tutorial
</title>
<para>
By deploying OpenJPA into a J2EE environment, you can maintain the simplicity
and performance of OpenJPA, while leveraging J2EE technologies such as container
managed transactions (JTA/JTS), enterprise objects with remote invocation (EJB),
and managed deployment of multi-tiered applications via an application server.
This tutorial will demonstrate how to deploy OpenJPA-based J2EE applications and
showcase some basic enterprise JPA design techniques. The tutorial's sample
application models a basic garage catalog system. While the application is
relatively trivial, the code has been constructed to illustrate simple patterns
and solutions to common problems when using OpenJPA in an enterprise
environment.
</para>
<section id="jpa_j2ee_tutorial_requirements">
<title>
Prerequisites for the OpenJPA J2EE Tutorial
</title>
<para>
This tutorial assumes that you have installed OpenJPA and setup your classpath
according to the installation instructions appropriate for your platform. In
addition, this tutorial requires that you have installed and configured a
J2EE-compliant application server, such as WebLogic or JBoss, running on JDK
1.5. If you use a different application server not listed here, this tutorial
may be adaptable to your application server with small changes; refer to your
application server's documentation for any specific classpath and deployment
descriptor requirements.
</para>
<para>
This tutorial assumes a reasonable level of experience with OpenJPA and JPA. We
provide a number of other tutorials for basic concepts, including enhancement,
schema mapping, and configuration. This tutorial also assumes a basic level of
experience with J2EE components, including session beans, JNDI, JSP, and
EAR/WAR/JAR packaging. Sun and/or your application server company may provide
tutorials to get familiar with these components.
</para>
<para>
In addition, this tutorial uses Ant to build the deployment archives. While this
is the preferred way of building a deployment of the tutorial, one can easily
build the appropriate JAR, WAR, and EAR files by hand, although that is outside
the scope of this document.
</para>
</section>
<section id="jpa_j2ee_tutorial_installation_types">
<title>
J2EE Installation Types
</title>
<para>
Every application server has a different installation process for installing
J2EE components. OpenJPA can be installed in a number of ways, which may or may
not be appropriate to your application server. While this document focuses
mainly upon using OpenJPA as a JCA resource, there are other ways to use OpenJPA
in a J2EE environment.
</para>
<itemizedlist>
<listitem>
<para>
<literal>JPA</literal>: J2EE 5 allows for the automatic injection of <classname>
EntityManager</classname> instances into the J2EE context.
</para>
</listitem>
<listitem>
<para>
Manual Binding into JNDI: Your application may require some needs in
initializing OpenJPA that go beyond the JPA and JCA specifications. In this
case, you can manually instantiate OpenJPA and place it into the JNDI tree. This
process, however, is not seamless and can require a fair bit of custom
application server code to bind an instance of <classname>
org.apache.openjpa.persistence.EntityManagerFactoryImpl</classname> into JNDI.
</para>
</listitem>
</itemizedlist>
</section>
<section id="jpa_j2ee_tutorial_installing_sample">
<title>
Installing the J2EE Sample Application
</title>
<para>
Installing the sample application involves first compiling and building a
deployment archive (.ear) file. This file then needs to be deployed into your
application server.
</para>
<section id="jpa_j2ee_tutorial_building_sample">
<title>
Compiling and Building The Sample Application
</title>
<para>
Navigate to the <filename>samples/persistence/j2ee</filename> directory of your
OpenJPA installation.
</para>
<para>
Ensure that the JNDI name in the setSessionContext() method in ejb/CarBean.java
matches your JCA installation. This defaults to <literal>java:/openjpa-ejb
</literal>, but the actual value will depend on the configuration of your JCA
deploy and your application server's JNDI context. E.g. the default name for a
WebLogic 9 install would be simply <literal>openjpa-ejb</literal>.
</para>
<para>
Compile the source files in place both in this base directory as well as the
nested <filename>ejb</filename> and <filename>jsp</filename> directories:
</para>
<programlisting>
javac *.java ejb/*.java jsp/*.java
</programlisting>
<para>
Enhance the Car class. (Optional)
</para>
<programlisting>
java org.apache.openjpa.enhance.PCEnhancer Car.java
</programlisting>
<para>
Run the mapping tool; make sure that your <filename> META-INF/persistence.xml
</filename> file includes the same connection information (e.g. Driver, URL,
etc.) as your JCA installation. You should update your JCA configuration to
include <classname>samples.persistence.j2ee.Car</classname> in the
<link linkend="openjpa.MetaDataFactory">MetaDataFactory</link> property:
</para>
<programlisting>
&lt;config-property name="MetaDataFactory"&gt;Types=samples.persistence.j2ee.Car&lt;/config-property/&gt;
</programlisting>
<programlisting>
java org.apache.openjpa.jdbc.meta.MappingTool Car.java
</programlisting>
<para>
Build an J2EE application archive by running Ant against the <filename>
build.xml</filename>. This will create <filename>
openjpa-persistence-j2ee-sample.ear</filename>. This ear can now be deployed to
your appserver. Be sure to add the class <classname>samples.j2ee.Car</classname>
to the <literal>openjpa.PersistentClasses</literal> OpenJPA configuration
property. This will automatically register the Entity.
</para>
<programlisting>
ant -f build.xml
</programlisting>
</section>
<section id="jpa_j2ee_tutorial_sample_jboss">
<title>
Deploying Sample To JBoss
</title>
<para>
Place the ear file in the <filename>deploy</filename> directory of your JBoss
installation. You can use the above hints to view the JNDI tree to see if
<classname>samples.j2ee.ejb.CarHome</classname> was deployed to JNDI.
</para>
</section>
<section id="jpa_j2ee_tutorial_sample_weblogic">
<title>
Deploying Sample To WebLogic 9
</title>
<para>
Place the ear file in the <filename>autodeploy</filename> directory of your
WebLogic domain. Production mode (see your startWebLogic.sh/cmd file) should be
set to false to enable auto-deployment. If the application was installed
correctly, you should see <computeroutput>openjpa-persistence-j2ee-sample
</computeroutput> listed in the Deployments section of the admin console. In
addition you should find <literal>CarHome</literal> listed in the JNDI tree
under <computeroutput>AdminServer-&gt;samples-&gt;j2ee-&gt;ejb</computeroutput>
. Ensure that you have added the class <classname>samples.j2ee.Car</classname>
to the <literal>openjpa.PersistentClasses</literal> OpenJPA configuration
property in the <literal>META-INF/ra.xml</literal> file.
</para>
</section>
</section>
<section id="jpa_j2ee_tutorial_using">
<title>
Using The Sample Application
</title>
<para>
The sample application installs itself into the web layer at the context root of
sample. By browsing to <computeroutput>
http://yourserver:yourport/openjpa-persistence-j2ee-sample</computeroutput>,
you should be presented with a simple list page with no cars. You can edit, add,
delete car instances. In addition, you can query on the underlying <classname>
Car</classname> instances by passing in an JPQL query into the marked form (such
as <computeroutput>select car from Car car where car.model="Some Model"
</computeroutput> ).
</para>
</section>
<section id="jpa_j2ee_tutorial_architecture">
<title>
Sample Architecture
</title>
<para>
The garage application is a simple enterprise application that demonstrates some
of the basic concepts necessary when using OpenJPA in the enterprise layer.
</para>
<para>
The core model wraps a stateless session bean facade around an entity. Using a
session bean provides both a remote interface for various clients as well as
providing a transactional context in which to work (and thus avoiding any
explicit transactional code).
</para>
<para>
This session bean uses the JPA's detachment capabilities to provide an automatic
Data Transfer Object mechanism for the primary communication between the
application server and the (possibly remote) client. The <classname>Car
</classname> instance will be used as the primary object upon which the client
will work.
</para>
<itemizedlist>
<listitem>
<para>
<literal>samples/persistence/j2ee/Car.java</literal>: The core of the sample
application. This is the entity class that OpenJPA will use to persist the
application data. Instances of this class will also fill the role of data
transfer object (DTO) for EJB clients. To accomplish this, <classname>Car
</classname> implements <classname>java.io.Serializable</classname> so that
remote clients can access cars as parameters and return values from the EJB.
</para>
</listitem>
<listitem>
<para>
<literal> samples/persistence/j2ee/jsp/SampleUtilities.java</literal>: This is
a simple facade to aggregate some common J2EE behavior into some static methods.
By placing all of the functionality into a single facade class, we can reduce
code maintenance of our JSPs.
</para>
</listitem>
<listitem>
<para>
<literal>samples/persistence/j2ee/ejb/Car*.java</literal>: The source for the
<classname>CarEJB</classname> session bean. Clients can use the <classname>
CarHome</classname> and <classname>CarRemote</classname> interfaces to find,
manipulate, and persist changes to <classname>Car</classname> transfer object
instances. By using J2EE transactional features, the implementation code in
<filename>CarBean.java</filename> can be focused almost entirely upon business
and persistence logic without worrying about transactions.
</para>
</listitem>
<listitem>
<para>
<literal>samples/persistence/j2ee/jsp/*.jsp</literal>: The web presentation
client. These JSPs are not aware of the JPA; they simply use the <classname>
CarEJB</classname> session bean and the <classname>Car</classname> transfer
object to do all the work.
</para>
</listitem>
<listitem>
<para>
<literal>samples/persistence/j2ee/resources/*</literal>: Files required to
deploy to the various appservers, including J2EE deployment descriptors, WAR/
EJB/ EAR descriptors, as well as appserver specific files.
</para>
</listitem>
<listitem>
<para>
<literal>samples/persistence/j2ee/build.xml</literal>: A simple Ant build file
to help in creating a J2EE EAR file.
</para>
</listitem>
</itemizedlist>
</section>
<section id="jpa_j2ee_tutorial_notes">
<title>
Code Notes and J2EE Tips
</title>
<orderedlist>
<listitem>
<para>
Entity classes are excellent candidates for the Data Transfer Object Pattern.
This pattern attempts to reduce network load, as well as group business logic
into concise units. For example, <methodname>CarBean.edit</methodname> allows
you to ensure that all values are correct before committing a transaction,
instead of sequentially calling getters and setters on the session facade. This
is especially true when using RMI such as from a Swing based application
connecting to an application server.
</para>
<para>
<classname>CarEJB</classname> works as a session bean facade to demarcate
transactions, provide finder methods, and encapsulate complex business logic at
the server level.
</para>
</listitem>
<listitem>
<para>
<methodname>EntityManager.close()</methodname> should be called at the end of
every EJB method. In addition to ensuring that your code will not attempt to
access a closed <classname>EntityManager</classname>, it allows OpenJPA to free
up unused resources. Since an <classname>EntityManager</classname> is created
for every transaction, this can increase the scalability of your application.
</para>
</listitem>
<listitem>
<para>
You should not use <methodname> EntityManager.getTransaction()</methodname> when
using JTA to manage your transactions. Instead, OpenJPA will integrate with JTA
to automatically govern transactions based on your EJB transaction
configuration.
</para>
</listitem>
<listitem>
<para>
While serialization of entity instances is relatively straightforward, there are
several things to keep in mind:
</para>
<itemizedlist>
<listitem>
<para>
While "default fetch group" values will always be returned to the client upon
serialization, lazily loaded fields will not as the <classname>EntityManager
</classname> will have been closed before those fields attempt to serialize. You
can either access those fields before serialization, configure the fetch type of
the relationships, or configure your JPQL queries to eagerly fetch data.
</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>
It is not necessarily required that you use EJBs and container-managed
transactions to demarcate transactions, although that is probably the most
common method. In EJBs using bean managed transactions, you can control
transactions through the <classname>javax.transaction.UserTransaction
</classname> interface. Furthermore, outside of session beans you can control
the JPA layer's transaction via the <classname>
javax.persistence.EntityTransaction</classname> interface.
</para>
</listitem>
<listitem>
<para>
<classname>EntityManager</classname>s are allocated on a per-Transaction basis.
Calling <methodname>getEntityManager</methodname> from the same <classname>
EntityManagerFactory</classname> within the same EJB method call will always
return the same entity manager, although the user-visible object may be proxied,
so might not compare equal using the <literal>==</literal> operator.
</para>
</listitem>
</orderedlist>
</section>
</section>
</chapter>