How to refresh an entity in a Future?

261 views
Skip to first unread message

Patrick Kaeding

unread,
May 4, 2011, 2:31:13 AM5/4/11
to play-fr...@googlegroups.com
(I am cross-posting this here from http://stackoverflow.com/questions/5865660/how-to-refresh-an-entity-in-a-future in the hopes of getting more eyeballs. If this is frowned upon, please let me know, and I will refrain from doing it in the future.)

I am not really sure where my problem lies, as I am experimenting in two areas that I don't have much experience with: JPA and Futures (using Play! Framework's Jobs and Promises).

I have the following bit of code, which I want to return a Meeting object, when one of the fields of this object has been given a value, by another thread from another HTTP request.  Here is what I have (from the body of a controller method):

Promise<Meeting> meetingPromise = new Job<Meeting> () {
@Override
public Meeting doJobWithResult() throws Exception {
Meeting meeting = Meeting.findById(id);
while (meeting.bbbMeetingId == null) {
Thread.sleep(1000);
meeting = meeting.refresh();    // I tried each of these
meeting = meeting.merge();      // lines but to no avail; I
meeting = Meeting.findById(id); // get the same result
}
return meeting;
}
}.now();
Meeting meeting = await(meetingPromise);

As I note in the comments, there are three lines in there, any one of which I think should allow me to refresh the contents of my object from the database.  From the debugger, it seems that the many-to-one relationships are refreshed by these calls, but the single values are not.

So, how can I refresh my model from the database?

Geoff Ward

unread,
May 4, 2011, 2:46:15 PM5/4/11
to play-framework
Hello Players,

I am having a similar problem, trying to use a Promise to see a change
to an Entity that gets saved by another request, without success.

No matter what, the EntityManager seems to have a copy of the old, non-
updated Entity Object in its session or context. In this case, the
Promise never sees the change.

I am new to JPA so this is a struggle, does anyone have any advice on
this problem?

On the Entity that is being saved, I am simply calling Plays' save()
method, should I call JPA.em().refresh() after saving? Or explicitly
call JPA.em().persist()? None of these seem to have an effect on the
EntityManger cache?

Thanks for your help,
Geoff

On May 4, 2:31 am, Patrick Kaeding <pkaed...@gmail.com> wrote:
> (I am cross-posting this here
> fromhttp://stackoverflow.com/questions/5865660/how-to-refresh-an-entity-i...

Guillaume Bort

unread,
May 5, 2011, 10:41:45 AM5/5/11
to play-fr...@googlegroups.com
You will always have race conditions in these cases as you will mix
multithreading with database transactions. Basically your job will
start before the main action terminates and so the job transaction
will begin before the commit of the action one.

> --
> You received this message because you are subscribed to the Google Groups "play-framework" group.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
>
>

--
Guillaume Bort, http://guillaume.bort.fr

Patrick Kaeding

unread,
May 5, 2011, 10:54:43 AM5/5/11
to play-fr...@googlegroups.com
Okay, I follow what you are saying.  So, how can I start a new transaction in my Job?  By design, I don't want my Job to hold on to a transaction for its entire life.  Is there a way to accomplish this?

Geoff Ward

unread,
May 5, 2011, 2:08:49 PM5/5/11
to play-framework
Thanks for the reply Guillaume (and for all of your work on Play!). So
it seems that using database access in a Job is not a good way to
check the state of objects. As an alternative, what do you think about
checking the Cache in a Job for a changed Object? I understand the
issues in a distributed environment where one would need Memcache
implemented but overall seems like it would work.

Patrick Kaeding

unread,
May 6, 2011, 12:37:58 AM5/6/11
to play-fr...@googlegroups.com
Hmm, the Cache idea might work.  I will consider that a fallback plan if I can't start a new transaction in my job.

In my reading, I found @NoTransaction (http://www.playframework.org/documentation/1.2.1/jpa#transactions) but if I simply annotate my action method with that, I get the following error:

A JPA error occurred (The JPA context is not initialized. JPA Entity Manager automatically start when one or more classes annotated with the @javax.persistence.Entity annotation are found in the application.): 


In a comment by 'bond', I see: 

  "Correction: @NoTransaction does not prevent transaction, it prevents JPA initialization."


So, that makes sense.  @NoTransaction is really intended for controller actions that don't hit the database.  If they don't need it, skipping the EM initialization will make things faster.


So, if I use that annotation, it seems I am on my own as far as setting up everything for JPA.  Is there a way to trigger initialization?  The error implies that it will happen automatically if @Entity classes are found, but that doesn't seem to hold in my case.


How can I initialize JPA explicitly?


Thanks!

Marc Deschamps

unread,
May 6, 2011, 4:39:59 AM5/6/11
to play-fr...@googlegroups.com
You could use the JPAPlugin class like this in your Job:

@Override

public void before(){

JPAPlugin.startTx(false);

}

@Override

public void after(){

JPAPlugin.closeTx(false);

}


Patrick Kaeding

unread,
May 6, 2011, 10:25:09 AM5/6/11
to play-fr...@googlegroups.com
Ahh, that did the trick for me.  Thanks Marc and Guillaume for all of your help!

In case it helps anyone else, here is what the code snippet that I pasted earlier looks like:

Promise<Meeting> meetingPromise = new Job<Meeting> () {

@Override

public Meeting doJobWithResult() throws Exception {

Meeting meeting = Meeting.findById(id);

while (meeting.bbbMeetingId == null) {

Thread.sleep(1000);

if (JPA.isInsideTransaction()) {

JPAPlugin.closeTx(false);

}

JPAPlugin.startTx(true);

meeting = Meeting.findById(id);

JPAPlugin.closeTx(false);

}

return meeting;

}

}.now();

Meeting meeting = await(meetingPromise);


I am not using the @NoTransaction annotation, because that messes up some other code that checks if the request is coming from a valid user.


Thanks!

Guillaume Bort

unread,
May 7, 2011, 6:15:05 AM5/7/11
to play-fr...@googlegroups.com
The problem is that JPA can't work without a transaction. It's a pain
but it's the way the JPA guy conceived it...

> --
> You received this message because you are subscribed to the Google Groups
> "play-framework" group.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to
> play-framewor...@googlegroups.com.

Patrick Kaeding

unread,
May 7, 2011, 1:33:14 PM5/7/11
to play-fr...@googlegroups.com
Thanks Guillaume, that explains a lot of the behavior I was seeing when I tried various combinations of committing/rolling back the transaction (and thus completing it) inside my loop.  The transaction was not active the next time around the loop, so everything blew up.

Creating a new transaction via JPAPlugin seems to work well, and I think I have a pretty good understanding now of *why* it is necessary.

Thanks for helping out with this issue, and thanks for creating Play!

Tony Xiao

unread,
May 9, 2011, 2:37:36 AM5/9/11
to play-fr...@googlegroups.com
I have a similar problem. I seem to be totally unable to operate on an object after awaiting for future.

public static void edit(JPABase entity) {
    if (entity == null)
        notFound();
    await(new F.Timeout(500));

if (JPA.isInsideTransaction()) {
JPAPlugin.closeTx(false);
   }
JPAPlugin.startTx(false);
entity.merge();
entity.save();
JPAPlugin.closeTx(false);
ok();

    }

Patrick Kaeding

unread,
May 9, 2011, 10:13:54 AM5/9/11
to play-fr...@googlegroups.com
Hi Tony

What error(s) are you getting?

From looking at the code you pasted, I suppose it is possible that the 'entity' is null when you get down to the 'entity.merge()' line (unless notFound() throws an exception, which is handled by some higher level).

If you could paste the stack trace, that might be helpful in figuring out what is going on.

Thanks
Reply all
Reply to author
Forward
0 new messages