0

We are using Spring Data (version 1.3.2) with OpenJPA (version 2.2.2) in our project. We are facing issue with lazily loaded fields in our entities for OneToOne relationship. I will describe the issue with an example:

We have two entities (say Person and Address). There is one to one relationship between Person and Address. Below given is the code sample for Person and Address entity (getters and setters left out for brevity):

@Entity
public class Person {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;

private String firstName;

private String lastName;

@OneToOne(mappedBy = "person", fetch = FetchType.LAZY)
private Address address;
}

@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;
}

We then created AddressRepository and PersonRepository by extending from JpaRepository to provide us the basic CRUD operations for these two entities.

In our application Person entity is created first without having any address associated with it. Address entity is then created at a later point in time by setting appropriate Person entity in Address instance. Until this point all works as expected.

Now if query Person entity using PersonRepository's findOne method by passing appropriate id then the returned Person instance does not have address field populated. When we access address field of Person that is returned by PersonRepository it happens to be null. We were expecting that address field in Person would be available if there is an Address entity associated with Person. Given that the fetch type is set to LAZY would just mean that it would be loaded only when we try to access address field. This is how indeed it works when we use JPA APIs directly (using EntityManager.find() method).

This same piece of code used to work fine when we were using hibernate. Due to some legal reasons we had to change our JPA provider to OpenJPA and this stopped working. Is there anything that we should do differently when using OpenJPA in order to get lazy loading of OneToOne working?

Note: We have L2 cache turned ON. We found out that there is an issue with L2 cache in OpenJPA and opened this defect on OpenJPA (https://issues.apache.org/jira/browse/OPENJPA-2522). However, even with this defect there is a workaround (evicting the entity from L2 cache using entitymanagerfactory's evict method) and using JPA APIs we can get it to work. However, with spring data we observed that this issue exists even when we use this workaround. We did this by providing entity-manager-factory-ref in jpa-repositories and then injecting same emf in our beans to evict the entity from cache:

<jpa:repositories base-package="com.abc.dsp.spring.data.repository" entity-manager-factory-ref="emf"/>

Due to widespread use of spring data in our project it is difficult to move to using pure JPA APIs. Also, like mentioned in some answers here and here we are trying to avoid to changing to eager fetch type or custom setter methods due to widespread code impact.

UPDATE: Using custom setter method is also not working. What is working is changing the fetch type to EAGER. However, we would like to keep fetchType to LAZY because in most cases we don't need the associated object and that object is quite heavy.

1 Answer 1

0

OK,so I figured out why the workaround (evicting entity from cache) was not working. I was trying to access address field from person outside the transaction boundary. I was querying Person using findOne and returning that instance from a method that was annotated with @Transactional annotation. I was trying to access the address field of returned Person instance in the calling method which was not annotated with @Transactional annotation. Once I accessed address field in the method with @Transactional annotation then everything worked once I used the workaround. I am just capturing the workaround here again for easy reference for anyone else:

1) Inject the EntityManagerFactory in JPA repositories by specifying the entity manager ref in the jpa:repositories tag

<jpa:repositories base-package="com.ge.dsp.spring.data.repository" entity-manager-factory-ref="emf"/>

This emf is the one that is created in your spring application context.

2) Now inject the same emf in your class that calls DAO methods annotated with @Transactional annotation. After you have successfully created the entity by calling corresponding save/create method on DAO, call following method on emf to evict the entity:

emf.getCache().evict(Class cls, Object primaryKey)

This will ensure that next findOne/findAll call will get cache miss and return properly constructed entity. Note that this has to be done because of bug in openJPA (https://issues.apache.org/jira/browse/OPENJPA-2522)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.