Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Open JPA - L2 Cache Issue and Workaround

DZone's Guide to

Open JPA - L2 Cache Issue and Workaround

· Integration Zone
Free Resource

Build APIs from SQL and NoSQL or Salesforce data sources in seconds. Read the Creating REST APIs white paper, brought to you in partnership with CA Technologies.

I ran into an interesting issue with OpenJPA in one of my projects. I thought it would be good to share the issue I faced and the workaround I found for benefit of anyone who might run into the same issue.

The issue comes up when we are trying to load an entity that has some relations with fetch type set to LAZY. This issue is observed only when L2 cache is turned ON. L2 cache is typically turned ON to improve performance and can be done by adding following property to persistence unit in persistence.xml:

<property name="openjpa.DataCache" value="true" />  

To explain the issue in detail I will use a model so that we can base our discussion around it. So first lets get that down:

ER model

As can be seen from above ER diagram Person has one to one relation with Address and one to many relation with PhoneNumber.
Relevant code snippet from entities is shown below

@Entity  
public class Person {  
     @Id  
     @GeneratedValue(generator = "person_seq", strategy = GenerationType.SEQUENCE)  
     private int id;  
   
     private String firstName;  
     
     private String lastName;  
     
     @OneToOne(mappedBy = "person", fetch = FetchType.LAZY)  
     private Address address;  
     
     @OneToMany(cascade = CascadeType.ALL, mappedBy = "person", orphanRemoval=true)  
     private List<PhoneNumber> phoneNumbers;  
     ....  
 } 

 @Entity  
 public class Address {  
     @Id  
     @GeneratedValue(strategy = GenerationType.AUTO)  
     private int id;  
 
     private String streetAddress;  
 
     private String city;  
 
     private String zipCode;  

     @OneToOne  
     @JoinColumn(name="PERSON_ID")  
     private Person person;  
     ...  
 } 


 @Entity  
 public class PhoneNumber {  
     @Id  
     @GeneratedValue(strategy = GenerationType.AUTO)  
     private int id;  
 
     private String number;  
   
     @OneToOne  
     @JoinColumn(name="PERSON_ID")  
     private Person person;  
     ...  
 } 


Problem

When we create a new instance of Person and Address, since Person is non-owing side of relation, we set Person instance in Address but don't set Address instance in Person (this is quite possible when Person and Address entities are persisted by different parts of application). We then persist both Person and Address instance. Later when we query Person entity by using EntityManager.find() method (may be in different part of application) the returned instance of Person doesn't have Address field set. This field doesn't get set even if we query Address entity independently. What this means is that applications depending on the lazy field to be loaded when they access it will run into NullPointerException. So in this case when you invoke Person.getAddress() then the returned value will always be null.This is because the Person instance that was cached was the one that did not have "Address" field set (the instance constructed during persistence of Person instance) and that instance never gets refreshed even after subsequent queries.

This issue was observed with OpenJPA 2.2.2. Looking up online revealed that there was a defect that was fixed on trunk that was related to L2 cache (https://issues.apache.org/jira/browse/OPENJPA-2285), which appeared to be the one that would solve this issue. However, even after back porting that fix in 2.2.2 branch same issue was seen.

Workaround

The only way I found that we can work around this issue is by evicting the entity under consideration from the cache after you have persisted the entity. So in the above case evicting Person instance after persisting (and committing the transaction) will make sure that subsequent find calls to entity manager would return an entity whose fields will be appropriately populated and we won't run into any NullPointerException.

// Here emf is EntityManagerFactory  
emf.getCache().evict(Person.class, this.personId);  

With above fix Person.getAddress() would return appropriate instance of Address when Person instance is obtained by using find method on EntityManager.

This workaround ensures that you can use l2 cache (and gain the performance improvements it brings) while making sure you won't run into any unexpected NullPointerExceptions when trying to access lazily loaded children/relations of an entity. If possible use this workaround in your application until a fix is available from OpenJPA to load the entity appropriately.

I have logged a defect against OpenJPA (https://issues.apache.org/jira/browse/OPENJPA-2522) to track this issue and have also submitted a test case to the community.

The Integration Zone is brought to you in partnership with CA Technologies.  Use CA Live API Creator to quickly create complete application backends, with secure APIs and robust application logic, in an easy to use interface.

Topics:

Published at DZone with permission of Atul Kshirsagar. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}