persistence@glassfish.java.net

Re: Question about the state of entities at rollback

From: David Van Couvering <david_at_vancouvering.com>
Date: Fri, 20 Apr 2007 10:51:10 -0700

Thanks, Ecmel, this does sound like it should work.

I am a little concerned about performance, because by closing the
EntityManager you lose the entire cache. All the caching work of a JPA
implementation is basically tossed out.

It makes me wonder: at least while you're staying in a single execution
thread, wouldn't you just do em.close() if there is an exception? You
could use ThreadLocal to put a different entity manager on each thread...

Also, although it's cool that TopLink handles lazy loading for detached
objects, this isn't portable, which to me in my environment is a
problem. If I were to switch to OpenJPA for example it could be that
all my code would start breaking because fields that used to be valid
are now null. Maybe this issue is something we could fix in the spec so
that you can rely on detached objects working with lazy loading across
implementations...

Thanks,

David

Ecmel Ercan wrote:
> Hi,
>
> I use JPA in Java SE, and after some trial and error I finally
> decided to use detached objects, considering the following
> assumptions:
>
> 1. EntityManagerFactory (emf) is thread safe.
> 2. EntityManager (em) is NOT tread safe but it is cheap to create one
> whenever you need.
> 3. NO exceptions in JPA is recoverable.
>
> So, I use only one emf per application and whenever I need to access
> persistence context I do the following:
>
> EntityManager em = emf.createEntityManager();
> EntityTransaction tx = null;
> try {
> tx = em.getTransaction();
> tx.begin();
> ...
> ...
> tx.commit();
> } catch(Exception ex) {
> if(tx != null && tx.isActive()) tx.rollback();
> } finally {
> em.close();
> }
>
> As soon as em.close() is called, your POJOs will become detached so
> there is no need to copy them explicitly.
>
> For lazy loading, TopLink has a special feature where the detached
> instances still have access in their proxies to retrieve additional
> dettached instances, so practically you don't have to do anything to
> reach lazy instances.
>
> Hope helps.
>
>
> On 4/19/07, David Van Couvering <david_at_vancouvering.com> wrote:
>> Hi, all.
>>
>> I've been implementing some code against JPA and I was trying to figure
>> out how to handle a transaction rollback. I wasn't sure if JPA is
>> responsible for rolling back the state of any objects in the persistence
>> context.
>>
>> The answer appears to be no, and I found this in the spec:
>>
>> ===
>> 3.3.2 Transaction Rollback
>>
>> For both transaction-scoped and extended persistence contexts,
>> transaction rollback causes all pre-existing managed instances and
>> removed instances[15] to become detached. The instances' state will be
>> the state of the instances at the point at which the transaction was
>> rolled back. Transaction rollback typically causes the persistence
>> context to be in an inconsistent state at the point of rollback. In
>> particular, the state of version attributes and generated state (e.g.,
>> generated primary keys) may be inconsistent. Instances that were
>> formerly managed by the persistence context (including new instances
>> that were made persistent in that transaction) may therefore not be
>> reusable in the same manner as other detached objects—for example, they
>> may fail when passed to the merge operation.
>> ====
>>
>> As an app writer, this statement is somewhat disconcerting.
>>
>> I was wondering if you could give me some guidance for how I can write
>> an app against JPA that handles rollback correctly.
>>
>> I'm writing a controller that interacts with entity objects, passing
>> them around to various methods, storing them in member variables, and so
>> on. I don't want to detach them, because (a) it is costly for me to copy
>> all the data into a separate, detached instance, and then during the
>> merge operation to have them copied back into the instance stored in the
>> persistence context and (b) I never know if I can access a field or
>> relationship because I don't know if the field or related instance has
>> been loaded yet. When I keep them attached, JPA takes care of this for
>> me.
>>
>> Then I perform a persist() operation and it fails and rolls back. This
>> means all my objects I've been passing around and storing in various
>> places are suddenly detached and "may be" in an inconsistent state
>> (which from the perspective of a program is the same as saying they
>> *are* in an inconsistent state), and thus are invalid.
>>
>> So now what do I do? Is there a recommended approach for dealing with
>> this? How does my application detect this in some global way, and
>> reloads all the instances it was keeping around from the persistence
>> context?
>>
>> The other concern I have is I'd like consumers of my controller to use
>> entity objects as POJOs without having to know or care if they are
>> entities. But the rollback semantics means my callers have to deal with
>> handling a rollback. Alternately I could pay the copy cost of detaching
>> my objects before passing them up to higher levels, but then my caller
>> will still have to deal with the semantics of detached objects (e.g.
>> lazy fields or associated instances may be null). I don't see any way to
>> expose entities directly to higher levels of my app that are
>> entity-unaware... Which means I have to wrap my entities into proxy
>> classes that handle all the JPA semantics internally and don't expose
>> this to the caller.
>>
>> I am concerned that the rollback consequences can slip past developers,
>> who will then build apps that behave very erratically after a
>> transaction rollback. I know it's in the spec, but having worked with
>> lots of developers, most of them don't read the spec, but instead
>> cut-and-paste from examples. It would be great if we did a blog about
>> this, and provided some code showing how to do it right... I could do
>> this, but before I do I thought I'd check with you all first for any
>> thoughts you have on this.
>>
>> Thanks!
>>
>> David
>>