Do I need to annotate JPA actions with @Transactional in Play Framework-1.x to prevent the connection leak?

247 views
Skip to first unread message

Aleksey Bykov

unread,
Jun 6, 2015, 10:02:04 AM6/6/15
to play-fr...@googlegroups.com

As described here (relevant parts),

https://www.playframework.com/documentation/1.3.x/jpa

Play will automatically start the Hibernate entity manager when it finds one or more classes annotated with the @javax.persistence.Entity annotation... When the JPA entity manager is started you can get it from the application code, using the JPA helper... Play will automatically manage transactions for you. It will start a transaction for each HTTP request and commit it when the HTTP response is sent.


As described here (relevant parts),

https://www.playframework.com/documentation/2.0/JavaJPA

There is no built-in JPA implementation in Play 2.0; you can choose any available implementation.. Every JPA call must be done in a transaction so, to enable JPA for a particular action, annotate it with @play.db.jpa.Transactional. This will compose your action method with a JPA Action that manages the transaction for you.


In my project, there are several data sources referred from different controllers, for example:

Action of the MeteoController.java:

public static void meteoDeviceObjects() {
   
Query query = JPA.em("meteo").createQuery("FROM MeteoDeviceObject m");
   
List<MeteoDeviceObjectController> meteoDeviceObjects =  query.getResultList();


    renderJSON
(meteoDeviceObjects);
}



Action of the ClientsController.java:

public static void connectedClients(String connected) {
   
Query clientQuery = JPA.em("default").createQuery("FROM Client c WHERE c.connected=:connected");
   
List<Client> connectedClientsList = clientQuery.setParameter("connected", connected).getResultList();


    renderJSON
(connectedClientsList);
}


I found the connection leak, even with a relatively small load:


Oops: PersistenceException An unexpected error occured caused by exception PersistenceException: org.hibern ate.exception.GenericJDBCException: Could not open connection
play.exceptions.UnexpectedException: Unexpected Error
        at play.Invoker$Invocation.onException(Invoker.java:244)
        at play.Invoker$Invocation.run(Invoker.java:306)
        at Invocation.HTTP Request(Play!)
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.Gener
icJDBCException: Could not open connection
        at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityMan
agerImpl.java:1387)
        at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityMan
agerImpl.java:1310)
        at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException
(AbstractEntityManagerImpl.java:1397)
        at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:62)
        at play.db.jpa.JPA.withTransaction(JPA.java:230)
        at play.db.jpa.JPA.withinFilter(JPA.java:195)
        at play.db.jpa.JPAPlugin$TransactionalFilter.withinFilter(JPAPlugin.java
:299)
        at play.Invoker$Invocation.withinFilter(Invoker.java:272)
        at play.Invoker$Invocation.run(Invoker.java:289)
        ... 1 more
Caused by: org.hibernate.exception.GenericJDBCException: Could not open connecti
on ...



The JPA.em() method gets the current entity manager that's associated with the thread, it's intended for use with the  @Transactional annotation. It will get whichever entity manager the annotation says, so if you say @Transactional(value = "mydb"), it will get the mydb entity manager. The @Transactional annotation manages these for you, so that's why there's no leak when you use the  em() method.
JPA.em(String) however does not get the entity manager from the thread, it always gets a new entity manager, hence you need to make sure that you clean it up by calling em.close() when you're finished with it (usually in a finally block).


Do I need to use @Transactional annotation in Play Framework-1.x to prevent the connection leak? And when I don't explicitly specifying entity manager? For example:

...
public static void update() {
   
Contact contact = new Contact ();


    contact
.FirstName = jsonContact.FirstName;
    contact
.LastName = jsonContact.LastName;
    contact
.Birthday = jsonContact.Birthday;
    contact
.Email = jsonContact.Email;
    contact
.Phone = jsonContact.Phone;
    contact
.Comment = jsonContact.Comment;


    contact
.save();
    renderJSON
(contact);
}
...


I would be very grateful for the information. Thanks to all.













Aleksey Bykov

unread,
Jun 6, 2015, 1:25:44 PM6/6/15
to play-fr...@googlegroups.com
My solution. If this is not precise - I would be grateful for other answers.

All actions (or on the controller's level) I'm annotated as 

@Transactional("NameOfRelevantEntityManagerHere")


For example:

@Transactional("NameOfEntityManagerHere")

public static void update() {
   
Contact contact = new Contact ();


    contact
.FirstName = jsonContact.FirstName;
    contact
.LastName = jsonContact.LastName;
    contact
.Birthday = jsonContact.Birthday;
    contact
.Email = jsonContact.Email;
    contact
.Phone = jsonContact.Phone;
    contact
.Comment = jsonContact.Comment;


    contact
.save();
    renderJSON
(contact);
}


I used this approach where there are calls to different entity managers:

@Transactional
public static void someAction() {
   
...
   
EntityManager entityManagerOne = JPA.em("NameOfEntityManagerOneHere");
   
try {
       
Query query = entityManagerOne.createQuery(...);
       
...
   
} finally {
        entityManagerOne
.close();
   
}


   
EntityManager entityManagerTwo = JPA.em("NameOfEntityManagerTwoHere");
   
try {
       
Query query = entityManagerTwo.createQuery(...);
       
...
   
} finally {
        entityManagerTwo
.close();
   
}
   
...
}

In my case this approach has solved the problem of the connection leaks.
Reply all
Reply to author
Forward
0 new messages