Нужно ли аннотировать действия JPA с помощью @Transactional в Play Framework-1.x, чтобы предотвратить утечку соединения?
Как описано здесь (соответствующие части),
https://www.playframework.com/documentation/1.3.x/jpa
Play автоматически запустит менеджер сущностей Hibernate, когда найдет один или несколько классов, помеченных
@javax.persistence.Entity
аннотация... Когда менеджер сущностей JPA запущен, вы можете получить его из кода приложения, используя помощника JPA... Play автоматически будет управлять транзакциями за вас. Он начнет транзакцию для каждого HTTP-запроса и подтвердит его при отправке HTTP-ответа.
Как описано здесь (соответствующие части),
https://www.playframework.com/documentation/2.0/JavaJPA
В Play 2.0 нет встроенной реализации JPA; Вы можете выбрать любую доступную реализацию. Каждый вызов JPA должен выполняться в транзакции, поэтому, чтобы включить JPA для определенного действия, аннотируйте его
@play.db.jpa.Transactional
, Это скомпонует ваш метод действия с действием JPA, которое управляет транзакцией для вас.
В моем проекте есть несколько источников данных от разных контроллеров, например:
Действие MeteoController.java
:
public static void meteoDeviceObjects() {
Query query = JPA.em("meteo").createQuery("FROM MeteoDeviceObject m");
List<MeteoDeviceObjectController> meteoDeviceObjects = query.getResultList();
renderJSON(meteoDeviceObjects);
}
Действие 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);
}
Я обнаружил утечку соединения даже при относительно небольшой нагрузке:
К сожалению: PersistenceException Произошла непредвиденная ошибка, вызванная исключением. PersistenceException: org.hibern ate.exception.GenericJDBCException: не удалось открыть соединение.
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
at org.hibernate.exception.internal.StandardSQLExceptionConverter.conver
t(StandardSQLExceptionConverter.java:54)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlException
Helper.java:124)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlException
Helper.java:109)
at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnec
tion(LogicalConnectionImpl.java:221)
at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnectio
n(LogicalConnectionImpl.java:157)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegi
n(JdbcTransaction.java:67)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(Ab
stractTransactionImpl.java:160)
at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:
1351)
at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:59)
... 6 more
Caused by: java.sql.SQLException: An attempt by a client to checkout a Connectio
n has timed out.
at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:77)
at org.hibernate.service.jdbc.connections.internal.DatasourceConnectionP
roviderImpl.getConnection(DatasourceConnectionProviderImpl.java:141)
at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectio
nAccess.obtainConnection(AbstractSessionImpl.java:301)
at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnec
tion(LogicalConnectionImpl.java:214)
... 11 more
Caused by: com.mchange.v2.resourcepool.TimeoutException: A client timed out whil
e waiting to acquire a resource from com.mchange.v2.resourcepool.BasicResourcePo
ol@1cb35c -- timeout at awaitAvailable()
at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicRes
ourcePool.java:1461)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(
BasicResourcePool.java:639)
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicR
esourcePool.java:549)
... 14 more
Я нашел некоторую информацию об этом: https://github.com/playframework/playframework/issues/2890
JPA.em()
метод получает текущий менеджер сущностей, связанный с потоком, он предназначен для использования с@Transactional
аннотаций. Это получит любой менеджер сущности, который говорит аннотация, так что если вы скажете@Transactional(value = "mydb")
, он получитmydb
менеджер организации.@Transactional
аннотация управляет этим для вас, поэтому при использованииem()
метод.
JPA.em(String)
однако не получает менеджера сущностей из потока, он всегда получает нового менеджера сущностей, поэтому вам нужно убедиться, что вы его очистили, вызвавem.close()
когда вы закончите с этим (обычно вfinally
блок).
Нужно ли использовать @Transactional
аннотация в Play Framework-1.x для предотвращения утечки соединения? И когда я не указываю явно менеджер сущностей? Например:
...
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);
}
...
Буду очень признателен за информацию. Спасибо всем.
1 ответ
Я использовал этот подход, когда есть вызовы к различным менеджерам сущностей:
@Transactional
public static void someAction() {
...
EntityManager entityManagerOne = JPA.em("NameOfEntityManagerOneHere");
try {
Query query = entityManagerOne.createQuery(...);
...
// other actions with entityManagerOne (before closing)
} finally {
entityManagerOne.close();
}
EntityManager entityManagerTwo = JPA.em("NameOfEntityManagerTwoHere");
try {
Query query = entityManagerTwo.createQuery(...);
...
// other actions with entityManagerTwo (before closing)
} finally {
entityManagerTwo.close();
}
...
}
И где менеджер сущностей отличается от стандартного:
@Transactional
public static void someAction() {
...
EntityManager entityManagerOne = JPA.em("NameOfEntityManagerOneHere");
try {
Query query = entityManagerOne.createQuery(...);
...
// other actions with entityManagerOne (before closing)
} finally {
entityManagerOne.close();
}
}
Для менеджера сущностей по умолчанию:
@Transactional
public static void someAction() {
Query query = JPA.em().createQuery(...);
...
}
В моем случае этот подход решил проблему утечки соединения.