Tuning Options
Query Optimizations
Some vendors allow an option to compile the associated SQL at build time, which can greatly improve performance. Also, it is possible to create named queries that enable specifying the SQL in the external configuration file. The SQL can then be tuned separately from the code.
Caching
The JPA specification defines two levels of caching: one that is scoped to the Entity Manager and another that is scoped to the Persistent Unit. The Entity Manager level cache is associated with a current transaction unless you are using the Extended Context—in which you can scope the cache to the life cycle of a Stateful Session Bean. We discussed this life cycle earlier in the "Programming Model" section. The JPA Entity Manager has a flush method you can invoke to clear the cache and push changes to the database.
The Entity Manager cache is there is for keeping data around during a multi-request flow, often called a "conversation;" however, the persistence unit cache is meant to really be a performance booster for read-only or read-mostly data. OpenJPA provides a data-level cache at the persistence unit level. OpenJPA provides a Single JVM cache provider. This may be ideal for read-only data that is initialized during the startup of an application. To use it, you can configure a cache provider on the persistence unit. Listing 8.64 shows an example of configuring the single JVM cache.
Listing 8.64. Caching
<persistence-unit name="pie-db-JAVA-SE"> <provider> org.apache.openjpa.persistence.PersistenceProviderImpl </provider> <properties> <property name="openjpa.MaxFetchDepth" value="5"/> <property name="openjpa.jdbc.MappingDefaults" value="StoreEnumOrdinal=false"/> <property name="openjpa.ConnectionURL" value="jdbc:derby://localhost:1527/PWTE"/> <property name="openjpa.ConnectionDriverName" value="org.apache.derby.jdbc.ClientDriver"/> <property name="openjpa.jdbc.DBDictionary" value="derby"/> <property name="openjpa.jdbc.Schema" value="APP"/> <property name="openjpa.DataCache" value="true"/> </properties> </persistence-unit>
After you configure the cache, you can configure whichever entity you need to cache. For example, in Listing 8.65 we cache the product data. In the listing, the timeout attribute of the annotation specifies when the provider should invalidate the cache from when the data was first created.
Listing 8.65. Life Cycle Methods
import org.apache.openjpa.persistence.DataCache; @Entity @NamedQuery(name="product.all",query="select p from Product p") @DataCache(timeout=6000000) public class Product implements Serializable { @Id @Column(name="PRODUCT_ID") protected int productId;
A.8.4
A single JVM often will not work for read-mostly cases or distributed cache facilities meant to deal with large volumes of data in a distributed fashion to alleviate database contention. There are some distributed cache technologies that specialize in these situations. IBM WebSphere Extended Data Grid [DataGrid] also allows you to work with distributed cache technologies as a second-level cache to OpenJPA, as well as supporting other patterns for using JPA with cached data.
OpenJPA also supports a QueryCache to cache the results of queries so that they can be reused without going back to the database. See the OpenJPA documentation for more details.
Loading Related Objects
OpenJPA supports both eager and lazy loading. We showed in the "Attributes" and "Relationships" sections that you can mark a field or relationship with a fetch type. In addition, OpenJPA supports fetch groups that allow you to link objects that are logically linked together in your application. If you have a class with two lazy fields, but you know in your application that when you load one, you will most likely load the other, you can configure a fetch group. Listing 8.66 shows how to load Customer and LineItem objects together whenever the other is accessed in the context of a lazily loaded Order.
Listing 8.66. Fetch Groups
@Entity @FetchGroups({ @FetchGroup(name="detail", attributes={ @FetchAttribute(name="lineItems"), @FetchAttribute(name="customers") }) class Order
This FetchGroups feature can be very important to minimize the number of round trips to the database at the same time that you minimize the initial load time of an object.
Locking
A.8.5
OpenJPA provides both configuring and an API option for affecting the desired locking level. For more details on database locking, see the paper titled "Locking Strategies for Database Access."
In addition, because OpenJPA supports disconnected patterns, you can use the @Version annotation to map a particular version column to a database. Listing 8.67 shows how simple this task can be.
Listing 8.67. Version Number
@Entity public class Order { @Id private String orderId; @Version private int version;
Remember that when an object becomes disassociated with its EntityManager, it becomes disconnected. When the object is reattached, OpenJPA will check whether the version number has changed in the database before performing the updates. When the JPA runtime detects an attempt to concurrently modify the same record, it throws an exception to the transaction attempting to commit last. This prevents overwriting the previous commit with stale data.
A version field is not always required, but without one, concurrent threads or processes might succeed in making conflicting changes to the same record at the same time.