Announcement: You can find the guides for Commerce 7.5 and later on the new Elastic Path Documentation site. This Developer Center contains the guides for Commerce 6.13.0 through 7.4.1.Visit new site

This version of Elastic Path Commerce is no longer supported or maintained. To upgrade to the latest version, contact your Elastic Path representative.

Java Persistence API (JPA)

Java Persistence API (JPA)

This is a guide for configuring persistence using the Java Persistence API (JPA). We have chosen to use the Apache OpenJPA implementation.

Note: Package renaming applied to OpenJPA source

As a workaround for an issue with WebLogic, it was necessary to rename the persistence.xml file to jpa-persistence.xml.

About JPA

Originally part of the JSR-220 Enterprise JavaBeans 3.0 specification, JPA was separated from EJB 2.0 to be a standalone specification for Java persistence. There are many successful implementations of JPA, including

Features of JPA include

  • Supports a POJO (Plain Old Java Object) persistence model.
  • Rich inheritance support.
  • Support for annotations or XML based mapping files.
  • Supports pluggable persistence providers.
  • Supported by Spring
  • Supports native SQL as well as the Java Persistence Query Language (JPQL)

Online documentation

As a starting point, you should take a look at the following documentation.

Entities and Identities

Entity Characteristics

Any application defined object with the following characteristics can be an Entity:

  • It can be made persistent
  • It has a persistent identity (i.e. a unique key)
  • It is not a primitive, a primitive wrapper of build-in object.

So in most cases a concrete or an abstract class can be an Entity.

Mixed inheritance is supported - the superclass and/or subclass of an Entity does not have to be an Entity.

Entity Identification

All Entities must have a persistent id (i.e. a database primary key). This is known as the entity identity (or persistence identity) JPA supports

  • Single field identity (like our long uidPk field)
  • Composite primary keys (provided by an Identity class)

The Entity Identity must be defined on the root Entity or mapped superclass of the hierarchy. In our domain model, we have the uidPk identity defined on the Persistable interface. Persistence classes need to implement the getUidPk() method with the @Id annotation and setUidPk() method.

JPA supports several strategies for primary key generation:

  • Auto - leave the decision up to the JPA implementation.
  • Identity - the database will assign an identity value on insert
  • Sequence - use a datastore sequence to generate a value
  • Table - use a sequence table to generate a field value

Table is the generator used in our object model, as this will work with all databases, and offers the best overall performance. In our persistence classes we define the generator table JPA_GENERATED_KEYS with columns ID and LAST_VALUE, with a name and primary key value matching the name of the DB table that the class will be persisted to. JPA uses this table to keep track of the last identifier value used to ensure the next one used is unique. You can configure the number of values to allocate in memory for each trip to the database (default is 50). Allocating values in memory allows the JPA runtime to avoid accessing the database for every sequence request.

The annotations on the getUidPk() method are as follows:

@Id
@Column(name = "UIDPK")
@GeneratedValue(strategy = GenerationType.TABLE, generator = TABLE_NAME)
@TableGenerator(name = TABLE_NAME, table = "JPA_GENERATED_KEYS",
                pkColumnName = "ID", valueColumnName = "LAST_VALUE", pkColumnValue = TABLE_NAME)

Packaging

JPA requires one XML file META-INF/persistence.xml which

  • Defines the name of the persistence unit
  • Defines the transaction strategy
  • Identifies the entities contained within a persistence unit
  • Defines persistence provider configuration
  • It can also define the location of XML mapping files

Here is a sample META-INF/persistence.xml file

<?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">

  <persistence-unit name="myopenjpa">

    <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>

    <class>com.elasticpath.domain.catalog.impl.AbstractPriceImpl</class>
    <class>com.elasticpath.domain.catalog.impl.BrandImpl</class>
    <class>com.elasticpath.domain.catalog.impl.CategoryImpl</class>
    <class>com.elasticpath.domain.catalog.impl.CategoryTypeImpl</class>
    <class>com.elasticpath.domain.catalog.impl.InventoryImpl</class>
    <class>com.elasticpath.domain.catalog.impl.ProductAssociationImpl</class>
    <class>com.elasticpath.domain.catalog.impl.ProductCategoryImpl</class>
    <class>com.elasticpath.domain.catalog.impl.ProductImpl</class>
    <class>com.elasticpath.domain.catalog.impl.ProductSkuImpl</class>
    <class>com.elasticpath.domain.catalog.impl.ProductTypeImpl</class>

    ...

    <properties>
      <property name="openjpa.ConnectionFactoryName"
                value="java:comp/env/jdbc/epjndi"/commerce-legacy/>
    </properties>

  </persistence-unit>

</persistence>

This example defines a persistence unit named "myopenjpa", identifies the provider as OpenJPA (org.apache.openjpa.persistence.PersistenceProviderImpl), lists the classes contained in the persistence unit, and sets the connection factory to java:comp/env/jdbc/epjndi.

OpenJPA Properties

In our standard persistence unit (META-INF/jpa-persistence.xml) we set the following OpenJPA properties:

               core/ep-persistence-openjpa-itests/src/main/resources/META-INF/jpa-persistence.xml
		<properties>
			<property name="openjpa.ConnectionFactoryName" value="java:comp/env/jdbc/epjndi"/commerce-legacy/>
			<property name="openjpa.Log" value="slf4j"/commerce-legacy/>
			<property name="openjpa.ConnectionFactoryProperties" value="PrettyPrint=true, PrettyPrintLineLength=120"/commerce-legacy/>
			<property name="openjpa.jdbc.EagerFetchMode" value="parallel"/commerce-legacy/>
			<property name="openjpa.jdbc.SubclassFetchMode" value="parallel"/commerce-legacy/>
			<property name="openjpa.DetachState" value="loaded(DetachedStateField=true)"/commerce-legacy/>
			<property name="openjpa.Multithreaded" value="true"/commerce-legacy/>
			<property name="openjpa.AutoDetach" value="close, commit, nontx-read"/commerce-legacy/>
			<property name="openjpa.jdbc.DBDictionary" value="SupportsSubselect=true, SupportsCorrelatedSubselect=false"/commerce-legacy/>
			<property name="openjpa.jdbc.FinderCache" value="false"/commerce-legacy/>

			<!-- OpenJPA 2.x compatibility against spec -->
			<property name="openjpa.Compatibility" value="convertPositionalParametersToNamed=true"/commerce-legacy/>

			<!-- Pre-Load Metadata -->
			<property name="openjpa.MetaDataRepository" value="Preload=true"/commerce-legacy/>

			<!-- Uncomment the section below to enable data caching, comment them out to disable -->
			<!--
			<property name="openjpa.DataCache" value="true(cacheSize=25000,softReferenceSize=0)"/commerce-legacy/>
			<property name="openjpa.RemoteCommitProvider" value="sjvm"/commerce-legacy/>
			<property name="openjpa.DataCacheTimeout" value="1000"/commerce-legacy/>
			<property name="openjpa.QueryCompilationCache" value="true(cacheSize=2500,softReferenceSize=0)"/commerce-legacy/>

			<property name="openjpa.QueryCache" value="true(cacheSize=10000,softReferenceSize=0)"/commerce-legacy/>
			<property name="openjpa.jdbc.QuerySQLCache" value="false"/commerce-legacy/>
			-->

			<!--  Don't use this in production!!!  -->
			<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema"/commerce-legacy/>

		</properties>

            

The following table gives a brief summary of the given settings:

Property Description
ConnectionFactoryName Tells OpenJPA the JDNI location of the Datasource file.

Log

Tells OpenJPA which logging library to use for issuing log messages.

ConnectionFactoryProperties

This is used to format the log output of SQL debug messages to a more readable form.

EagerFetchMode

Set to "parallel" which was found to be the most efficient eager fetch mode for our domain model.

SubclassFetchMode Set to "parallel" which was found to be the most efficient subclass fetch mode for our domain model.
DetachState Tell OpenJPA to take advantage of a detached state field to make the attach process more efficient. This field is added by the enhancer and is not visible to your application. The loaded value tells OpenJPA to detach all fields and relations that are already loaded, but don't include unloaded fields in the detached graph. Setting DetachedStateField to true tells OpenJPA to use a non-transient detached state field so that objects crossing serialization barriers can still be attached efficiently. This requires, however, that your client tier have the enhanced versions of your classes and the OpenJPA libraries.

Multithreaded

This tells OpenJPA that persistent instances and OpenJPA components other than the EntityManagerFactory will be accessed by multiple threads at once.

AutoDetatch

This tells OpenJPA to detach entities on transaction commits, instances are read non-transactionally, or when the OpenJPA interface closes.

DBDirectory Tells OpenJPA that the database supports subselects in queries, but not correlated subselects.
DataCache Tells OpenJPA to use its own data cache for persistent level caching.
RemoteCommitProvider Sets OpenJPA to use a single JVM.
DataCacheTimeout Sets the data cache timeout to 1 second.
QueryCompilationCache Tells OpenJPA to cache parsed query strings. As a result, most queries are only parsed once in OpenJPA, and cached thereafter.
FinderCache Tells OpenJPA not to use the FinderCache. The FinderCache currently has a bug which causes data corruption issues and must be disabled.
QueryCache Tells OpenJPA to cache the object ids returned by query executions.
QuerySQLCache Set to false so that OpenJPA does not cache the SQL queries it generates.
Compatibility Tells OpenJPA to convert positional parameters to named parameters to support mixed queries, as well as to use OpenJPA 1 behaviour for Detached state serialization
MetaDataRepository Tells OpenJPA to preload the metadata repository as a runtime performance optimization

Class Enhancement

In order to provide optimal runtime performance, flexible lazy loading, and efficient, immediate dirty tracking, OpenJPA uses an enhancer. The enhancer post-processes the bytecode generated by your Java compiler for your domain classes, adding the necessary fields and methods to implement the required persistence features. This bytecode modification perfectly preserves the line numbers in stack traces and is compatible with Java debuggers.

Annotations

Overview

Most annotations are placed directly into the class files of the objects to be persisted. This makes it easy to maintain consistency between the objects and their persistence definition - there is no need to go find the "mapping file". Annotations are generally quicker to type than XML mapping definitions and IDE's like Eclipse support annotations including syntax checking and auto-completion. The exception to the rule is annotations that define named queries (see below).

Annotations can be at the field level (persistence layer writes directly to the field) or at the property level (persistence layer uses the getter and setter). You cannot mix field level and property level annotations within the same class hierarchy.

As a standard our object model uses property level annotations. So the annotations in the class files are written above the getter method for each property.

Tip: Read the full documentation

The OpenJPA User's Guide contains comprehensive documentation on all of the annotations available along with what parameters they take. You will need to refer to this often when doing your own mappings.

Simple annotations

Here's a list of some of the more common simple annotations:

Annotation Purpose

@Entity

denotes an entity class

@Table(name = "tablename")

denotes that the entity should persist to the tablename table in the schema

@Id

denotes a simple identity field

@Basic

denotes a simple value that should be persisted as-is

@Column(name = "columnname")

denotes that the value should be persisted to the columnname column in the schema

@Temporal

defines how to use Date fields at the JDBC level

@Enumerated

control how Enum fields are mapped

@Transient

specifies that a field is non-persistent

Note that properties in a class defined as an Entity that do not have any annotations may still be treated as persistable depending on the type. To avoid any confusion, ensure you annotate all property getters including @Basic and @Transient properties!

Relationship management

Here's a list of some of the annotations commonly used for mapping relationships in JPA

Annotation Purpose

@OneToOne

When an entity A references a single entity B, and no other A's can reference the same B, we say there is a one to one relation between A and B

@OneToMany

When an entity A references multiple B entities, and no two A's reference the same B, we say there is a one to many relation from A to B

@ManyToOne

When an entity A references a single entity B, and other A's might also reference the same B, we say there is a many to one relation from A to B

@ManyToMany

When an entity A references multiple B entities, and other A's might reference some of the same B's, we say there is a many to many relation between A and B

@Embedded

Embedded fields are mapped as part of the datastore record of the declaring entity. A class can be marked as embeddable by adding the @Embeddable annotation to the class

Inheritance

JPA provides two annotations for supporting inheritance

Annotation Purpose

@MappedSuperclass

a non-entity class that can define persistent state and mapping information for entity subclasses. Mapped superclasses are usually abstract. Unlike true entities, you cannot query a mapped superclass, pass a mapped superclass instance to any EntityManager or Query methods, or declare a persistent relation with a mapped superclass target

@Inheritance

Used to indicate the strategy for a hierarchy of entities. There are 3 strategies to chose from

  • SINGLE_TABLE - maps all classes in the hierarchy to the base class' table
  • JOINED - uses a different table for each class in the hierarchy. Each table only includes state declared in its class. Thus to load a subclass instance, the JPA implementation must read from the subclass table as well as the table of each ancestor class, up to the base entity class.
  • TABLE_PER_CLASS - uses a different table for each class in the hierarchy. Unlike the JOINED strategy, however, each table includes all state for an instance of the corresponding class. Thus to load a subclass instance, the JPA implementation must only read from the subclass table; it does not need to join to superclass tables.

OpenJPA specific annotations

OpenJPA provides some useful annotations in addition to those provided by the JPA specs. Many of these were required in our system due to the complexity of the object domain model. An overview of the OpenJPA specific annotations commonly used are as follows:

Annotation Purpose

@ForeignKey

Foreign key definition. It is important this annotation is present when there is a DB foreign key so that OpenJPA can calculate the correct order to issue database statements without violating key constraints.

@Dependent

Marks a direct relation as dependent. This means the referenced object is deleted whenever the owning object is deleted, or whenever the relation is severed by nulling or resetting the owning field

@ElementJoinColumn

Array, collection, or map element join column

@ElementForeignKey

Define a foreign key constraint to the columns of a collection element

@ElementDependent

Marks the entity elements of a collection, array, or map field as dependent. This means the referenced object is deleted whenever the owning object is deleted, or whenever the relation is severed by nulling or resetting the owning field

@Factory

Contains the name of a method that will be invoked to instantiate the field from the external form stored in the database.

@Externalizer

Sets the name of a method that will be invoked to convert the field into its external form for database storage

Example

Here is an example of an annotated class file which shows many of the annotation types in use

@Entity
@Table(name = "TPRODUCT")
public class ProductImpl extends AbstractEntityImpl implements Product {

  ...

  @Basic
  @Column(name = "START_DATE")
  public Date getStartDate() {
    return this.startDate;
  }

  @Version
  @Column(name = "LAST_MODIFIED_DATE")
  @Temporal(TemporalType.TIMESTAMP)
  public Date getLastModifiedDate() {
    return lastModifiedDate;
  }

  @ManyToOne(targetEntity = ProductTypeImpl.class)
  @JoinColumn(name = "PRODUCT_TYPE_UID")
  public ProductType getProductType() {
    return this.productType;
  }

  @Transient
  public ProductSku getDefaultSku() {
    if (defaultSku == null && productSkus != null && productSkus.size() > 0) {
      return (ProductSku) productSkus.values().iterator().next();
    }
    return defaultSku;
  }

  @OneToMany(targetEntity = ProductPriceImpl.class,
             cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
  @MapKey(name = "currencyCode")
  @ElementJoinColumn(name = "PRODUCT_UID")
  public Map getProductPrices() {
    return productPrices;
  }

  ...

}

Java Persistence Query Language (JPQL)

JPQL overview

JPQL executes over the abstract persistence schema (the entities you've created) defined by your persistence unit, rather than over the database like traditional SQL.

JPQL supports a SQL like syntax

SELECT [PD:<result>]
    [PD:FROM <candidate-class(es)>]
    [PD:WHERE <filter>]
    [PD:GROUP BY <grouping>]
    [PD:HAVING <having>]
    [PD:ORDER BY <ordering>]

JPQL supports dynamic and static (named) queries

Query basics

A simple query for all CategoryTypeImpl entities

SELECT ct FROM CategoryTypeImpl ct

The optional where clause places criteria on matching results. For example

SELECT j FROM ImportJobImpl j WHERE j.name = '01-SnapItUp'

And of course you can specify parameters, for example

SELECT c FROM CategoryImpl c WHERE c.code = ?1

Fetch joins

JPQL queries may specify one or more join fetch declarations, which allow the query to specify which fields in the returned instances will be pre-fetched

SELECT ct FROM CategoryTypeImpl ct JOIN FETCH ct.categoryAttributeGroupAttributes

Multiple fields may be specified in separate join fetch declarations. You may want to use other join types depending on the data, e.g. left outer join fetch.

Named queries

JPA supports the concept of named queries. While named queries can be defined in the annotations of a class (usually the class in which the named query is most relevant), they can also be defined external to the implementation class source files in XML files. In this case, each Java package has its own named queries XML file in META-INF/, entitled <packagename>-orm.xml. This is useful for keeping all the named queries specific to a package in the same place while allowing easier extensibility, so that queries can be modified without requiring the application to be recompiled.

Here is an example of named queries in an XML file:

<?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">
    <package>com.elasticpath.domain.customer.impl</package>
    <entity class="CustomerDeletedImpl">
        <named-query name="CUSTOMER_UIDS_SELECT_BY_DELETED_DATE">
            <query>select pd.customerUid from CustomerDeletedImpl as pd where pd.deletedDate &gt;= ?1</query>
        </named-query>
    </entity>
    <entity class="CustomerGroupImpl">
        <named-query name="CUSTOMERGROUP_SELECT_ALL">
            <query>select cg from CustomerGroupImpl cg</query>
        </named-query>
        <named-query name="CUSTOMERGROUP_FIND_BY_NAME">
            <query>select cg from CustomerGroupImpl cg where cg.name = ?1</query>
        </named-query>
    </entity>
</entity-mappings>

Persistence coding

Entity Manager

One of the key interfaces in the Java Persistence API is the EntityManager. This is similar to Hibernate's Session interface, providing services for CRUD operations (persist, remove, find, merge etc), creating Query instances and interfacing to the Transaction API.

Spring integration

Spring provides JPA integration, implementing JPA Container responsibilities. It includes beans like LocalEntityManagerFactoryBean which make it easy to configure the Entity Manager Factory through Spring configuration files, and SharedEntityManagerBean which provides a shared EntityManager reference.

Core Persistence model integration

JPA has been integrated with Elastic Path's core persistence model, which makes considerable use of Spring Inversion of Control and Dependency Injection.

Here is a summary of the core classes that you'll use for persistence. You can find these in the com.elasticpath.persistence.openjpa.impl package of ep-persistence-openjpa

Class Implements core interface Purpose How it works

JpaPersistenceEngineImpl

PersistenceEngine

The main persistence engine class. This is what you would set as the persistenceEngine property in your Spring configuration files for the service beans

This class uses a shared EntityManager provided by the Spring configuration

JpaSessionImpl

PersistenceSession

This is used by code such as the Import Manager which uses the PersistenceSession interface to run queries on the session in a transaction under it's control

This class uses the EntityManager methods directly

JpaQueryImpl

Query

This is used by code such as the Import Manager which uses the Query interface to manipulate queries within it's own transaction

This class uses the javax.persistence.Query methods directly

JpaTransactionImpl

Transaction

This is used by code such as the Import Manager which uses the Transaction interface control it's own transactions

This class uses the EntityTransaction methods directly

The majority of the time you'll just use the persistence-api PersistenceEngine interface, and configure Spring to inject the JPA implementation, so your code generally doesn't need to know anything about JPA.

Eager vs. Lazy loading

You can define fields to load lazily or eagerly the annotations. Be aware that using eager loading for everything can seriously affect performance and is unnecessary in cases when you don't need every linked object.

Lazy loading will defer loading of a field's associated entity until it is accessed, but it must be accessed while the connection is still open. Trying to access that field after the connection is closed will throw a NullPointerException.

Best practice is to define all (non-basic) fields as lazy loading (the default for OneToMany and ManyToMany mappings) and then define named queries with fetch join to eagerly load the relation fields. When you need just the basic data you use a query without the fetch join and when you need everything you use the fetch join query.

For example look at the following named queries (shown in class annotation format rather than the preferred XML format):

@NamedQuery(name = "CATEGORY_SELECT_BY_GUID",
              query = "SELECT c FROM CategoryImpl c WHERE c.code = ?1")

and

@NamedQuery(name = "CATEGORY_SELECT_BY_GUID_EAGER",
              query = "SELECT c FROM CategoryImpl c "
                    + "LEFT OUTER JOIN FETCH c.localeDependantFieldsMap "
                    + "LEFT OUTER JOIN FETCH c.attributeValueMap "
                    + "WHERE c.code = ?1")

You would use the first one when you just want the basic Category information, and the second when you need all the locale dependent fields and attributes.

Fetch Groups

OpenJPA provides the concept of Fetch Groups - sets of fields that load together. They can be used to to pool together associated fields in order to provide performance improvements over standard data fetching. Specifying fetch groups allows for tuning of lazy loading and eager fetching behavior. Fetch groups are used in our system for objects that are indexed by our Search Server to define exactly what fields need to be loaded by the indexing jobs.

Problems and work-arounds

JPA is designed to be simple and easy to use, so it is less complex than Hibernate. Sometimes this means that mappings that were possible in Hibernate are not possible to do in JPA without some modifications to the object model.

Locale and Currency

JPA does not support the Locale and Currency types so some special OpenJPA annotations are required when using these datatypes.

For example, here are annotated getters for Locale and Currency fields:

@Persistent(optional = false)
  @Externalizer("toString")
  @Factory("org.apache.commons.lang.LocaleUtils.toLocale")
  @Column(name = "LOCALE", length = 20)
  public Locale getLocale() {
    return locale;
  }

  @Persistent
  @Column(name = "CURRENCY", length = 3)
  @Externalizer("getCurrencyCode")
  @Factory("com.elasticpath.commons.util.impl.UtilityImpl.currencyFromString")
  public Currency getCurrency() {
    return currency;
  }

This tells JPA the externalizer method to use to convert the field into its external form for database storage (eg getCurrencyCode()) for Currency), and to the full class and method to instantiate the field from the external form stored in the database.

Constructors

JPA requires all entities to have a default constructor which it will call when the persistence layer starts up. If there is no default constructor the build-time enhancer will add one for you.

Where you need to be careful is if you have code in your constructor that is expecting other objects to have already been instantiated. For example, this code was originally in the constructor for ShoppingCartImpl

public ShoppingCartImpl() {
  super();
  this.shippingServiceLevelList = new ArrayList();
  this.viewHistory = (ViewHistory) getElasticPath().getBean("viewHistory");
}

Before this class was annotated as a JPA Entity the constructor wasn't being called until the ElasticPathImpl bean factory added a bean for ShoppingCartImpl, so the call to getElasticPath() would always work. However, once this class was added to the list of persistence entities, the constructor was being called before ElasticPathImpl was being instantiated, so the call to getElasticPath() would fail!

The work around is to put any code in the constructor that relies on other objects in a more appropriate place. In this particular case the code to get the viewHistory bean was moved into the getViewHistory method of ShoppingCartImpl.

Useful tools

The OpenJPA Maven plugin includes some useful tools that can help with using JPA.

Mapping Tool

The mapping tool can generate a schema from your object model. It is very useful for checking if your annotations map the object(s) to the current schema, or for generating the SQL if you add a new object.

You can run the tool by calling the following Maven command in the ep-core directory:

mvn -Dopenjpa.ConnectionDriverName=com.mysql.jdbc.Driver openjpa:sql

The generated file, database.sql, can be found in ep-core\target.

Logging

Detailed logging for JPA can be enabled by adding the following to your log4j.properties files

log4j.category.openjpa.jdbc.SQL=TRACE
log4j.category.openjpa.Runtime=TRACE
log4j.category.openjpa.MetaData=TRACE
log4j.category.openjpa.Enhance=TRACE
log4j.category.openjpa.Query=TRACE

These values should be removed or set to ${ep_log_level} for distribution

Required libraries

The following libraries, which can be found in your local repository (by default, ~/.m2/repository), are required for use by JPA:

GroupId ArtifactId Version Purpose

org.apache.openjpa

openjpa

2.3.0-ep2.0

Main Apache OpenJPA API

org.apache.geronimo.specs

geronimo-jta_1.1_spec

1.1

JTA specifications API

org.apache.geronimo.specs

geronimo-jms_1.1_spec

1.0.1

JMS specifications API

org.apache.geronimo.specs

geronimo-jpa_2.0_spec

1.1

JPA specifications API

net.sourceforge.serp

serp

1.14.1

Framework for manipulating Java bytecode

commons-collections

commons-collections

3.2

Newer release of the commons collections API

commons-pool

commons-pool

1.5.4

Object pooling components