Replace Play @Transactional with something better 10

Posted by Jens J├Ąger on November 30, 2013

One of the jpa issues I found in play 2 is the behaviour of the transaction handling.

The default play @Transactional does some kind of magic commit.

Lets have a look at the following controller action.

The problem is the variable task.name is changed in the hibernate persistence context and the change is saved on transaction commit. Of course you can use @Transactional(readOnly = true). But it’s easy to get this wrong.

Of course using readonly = true is not possible on actions they might only update a model under some conditions. In this case you might just confuse a = with == and you could have a nasty data changing bug in your application.

A much better approach would be: Only commit a transaction when an explicit save, update or delete is called from your model. The best way to realize this is a needsCommit thread local variable. The variable will be set to false by the transaction wrapper and to true inside the model methods.

The annotation an the call would look like the original play solutions:

The implementation of the withTx method looks like this:

In your model you need to set the commit variable to true like this:

Now you have a much more solid solution for your transactional handling. That only commits when you call a save in your model.

You find the code in the play4jpa project on github.

Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. JJ Mon, 02 Dec 2013 20:28:55 CET

    Nice thanks for the code.

    It’s sad because all the integration with ebean was already there.

    We will have to write our own finder (or copy the one in play 1.0)

    I’ll check your code on github to see if I integrate it in my project.

  2. Jens Mon, 02 Dec 2013 21:31:12 CET

    There is already a basic implementation of a play jpa finder:

    https://gist.github.com/jensjaeger/93da1c8c0bf8f627fae3

    I will integrate a more advanced version of the finder in the play4jpa project soon. Hopefully I also find time to blog about it as well.

    Happy coding
    Jens

  3. JJ Thu, 05 Dec 2013 17:46:38 CET

    Yeah, really good work.

    I just don’t know if I should go with EBean, JPA or your version of JPA. Because with the next update of play, it’s sure that my code will be incompatible with the new api(if I use any) unless they use your project as a base.

    But Ebean will still be supported as a plugin.

    It’s difficult to choose because it will be used in a really critical component of our system.

  4. JJ Thu, 05 Dec 2013 17:57:35 CET

    This implementation is only a snippet and not in your project yet ?

  5. Jens Thu, 05 Dec 2013 22:37:34 CET

    Hey JJ,

    thanks for your comments.

    If you use Hibernate you will be compatible with any 2.x Version of Play. In 2.3 JPA will be the default in favor of ebean. If you use the @Tx Annotation instead of the default play @Transactional you still compatible, but don’t use the withTransaction Part of Play Framework you use a custom one with another (i think better) behavior.

    After some research I decided that for me hibernate is a better foundation for a new project than ebean. Other think the same. There is a reason that eBean will be replaced with JPA in Play 2.3.

    The @Tx implementation in this post is extracted from a big client project im working on at the moment. In the future I will write some more about our hibernate jpa implementations here.

    Jens

  6. JJ Fri, 06 Dec 2013 19:18:04 CET

    Yess, I know it’ll be compatible with the JPA annotation. It’s just, it won’t come with all the play magic, or I’ll have to recode some part of the app.

  7. Jens Sun, 08 Dec 2013 15:16:48 CET

    Hey JJ,

    there is no more JPA related magic in play 2, like you know from play 1.

    Jens

  8. JJ Tue, 17 Dec 2013 21:23:47 CET

    I decided to use your gist for JPA finder and it work great.

    The only thing I couldn’t get to work is with @ManyToOne

    If I have en entity with 2 variables

    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    public Long id;
    @ManyToOne(fetch=FetchType.EAGER)
    public OtherModel ortherModel;

    .eq(“otherModel.code”, “value”)

    It shows a could not resolve property otherModel.code
    Maybe there’s something I don’t know in jpa. Because in JPQL it would work but I don’t know Criteria api.

    Do you have an idea ?

  9. JJ Wed, 18 Dec 2013 14:31:12 CET

    I was able to make it work, but I needed to add a method join() to your model.

    See it there : https://gist.github.com/miclefebvre/16b26137d886652e65d8

    It’s not as nice as in Ebean but it does the job
    If we have 4 tables it will look like

    find.where()
    .join(“tableA”, “tableA”)
    .join(“tableA.tableB”, “tableB”)
    .join(“tableB.tableC”, “tableC”)
    .eq(“tableC.value”, value)

    It would have been nice and easy to have the Finder create the join automatically if it detect if the field in eq (for example) as multiple dot (.) tableA.tableB.tableC but it wont work if we add other restriction like:

    .or(Restrictions.isNull(“tableA.Table.date”),
    Restrictions.lt(“tableA.Table.date”, now))

    Let me know if you have other idea.

  10. Jens Sun, 29 Dec 2013 16:32:01 CET

    Hey JJ,

    a extended version of the finder was pushed to https://github.com/jensjaeger/play4jpa. A blog post, unit test and more documentation is in progress.

    Jens

Comments

Information about Data protection