Tuesday, October 28, 2008

Persisting an Array with OpenJPA

One of the key features of JPA is the ability to add metadata annotations to a POJO such that it can be used interchangably in a persistence enabled and/or non-persistent environment.  The JPA 1.0 specification provides persistence metadata for most of the common data structures and object/entity relationships.  The specification clearly defines that collection-valued persistent fields and properties must be defined as one of: java.util.Collection, java.util.Set, java.util.List, or java.util.Map.  Notice there is no mention of the standard array type (ex.  MyObjectArray[]).  That means if your POJO contains a field that is an array you'll typically need to wrapper that array with one of the collection classes above, tag it as a relationship (most likely a OneToMany) and expose that collection type as a persistent property or change to use one of the supported collection classes.  While wrappering a collection is fairly simple; a) it is a logic change (vs. adding metadata) which may undesirable and b) it can have performance impacts due to copying references from one structure to another. 

If you are like me and that doesn't sit well with you, OpenJPA provides a extension, @org.apache.openjpa.persistence.PersistentCollection which can be used to persist an array.  OpenJPA does this by using a separate "container table" to store array elements.  If maintaining the order of the array is also important (which is typically the case), OpenJPA provides the @org.apache.openjpa.persistence.jdbc.OrderColumn extension.  OrderColumn specifies the column in the container table which will be used to store the index of the array entries.  If you don't like the default container table you can also customize that using the @org.apache.openjpa.persistence.jdbc.ContainerTable extension.

Here's a bit of incomplete code which uses PersistentCollection and OrderColumn to persist a deck of cards stored as an array.  Also, note that PersistentCollection supports cascade and fetch configuration, similar to the JPA relationship annotations.

import org.apache.openjpa.persistence.PersistentCollection;
import org.apache.openjpa.persistence.jdbc.OrderColumn;

@Entity
public class Deck {
    @PersistentCollection(elementCascade=CascadeType.PERSIST,
            fetch=FetchType.EAGER)
    @OrderColumn(name="cardorder")
    private Card[] cards;
// ...
}

@Entity
public class Card {
    @Basic
    private Suit suit;  // Suit is an enum Diamonds, Spades, ...
    
    @Basic
    private Rank rank;  // Rank is an enum Two - Ace
//...
}

If you'd like full source for a simple application based on the entities above email me at techhusky@gmail.com.  For more examples search the OpenJPA unit test source code for the @PersistentCollection annotation.  You'll find that OpenJPA persistent collection support is very configurable via a host of other OpenJPA annotations and can be used to persist other collection types in addition to the array.

-Jeremy

4 comments:

Unknown said...
This comment has been removed by the author.
Unknown said...
This comment has been removed by the author.
Unknown said...

This question has probably bugged you more than enough already, but I couldn't find the answer by a web search:

Officially, what evrsion of Open JPA does WAS 7.0 support? Is it 1.2.0 ?

We were thinking of migrating from WAS 6.1 to WAS 7.0, because
1) We use extensive pagination using Oracle 10g native queries

2) We want to replace Native SQL with JPQL. Unableto do this due to these problems:

A) WAS 6.1 supports JPA 1.0.1, which has bugs in setMaxResults and Query Pagination

B) I know these bugs have been resolved in Open JPA 1.1.0, but I couldn't find what JPA version does wAS 7.0 support.

Kevin Sutter said...

Avaneesh,
WAS 7.0 supports the OpenJPA 1.2.x service stream. If the problems you are experiencing are resolved in 1.1.x, then you should be fine with moving to WAS 7.0 and OpenJPA 1.2.x.

Thanks,
Kevin