У меня есть проект, работающий на Spring Boot 1.3.8, Hikari CP 2.6.1 и Hibernate (Spring ORM 4.2.8). Код на уровне обслуживания выглядит так:
public void doStuff() {
A a = dao.findByWhatever();
if (a.hasProperty()) {
B b = restService.doRemoteRequestWithRetries(); // May take long time
}
a.setProp(b.getSomethig());
dao.save(b);
}
Конфигурация Hikari имеет это: spring.datasource.leakDetectionThreshold=2000.
Проблема в том, что внешняя служба REST работает довольно медленно и часто отвечает за 2+ секунды, в результате мы видим много java.lang.Exception: Apparent connection leak detected, которые являются не чем иным, как ложными отрицательными значениями, хотя проблема хорошо видна: мы удерживаем соединение с БД для время, когда мы выполняем запрос на отдых.
Возникает вопрос: как правильно разделить DB и REST вещи? Или как сказать спящему режиму, чтобы освободить соединение между ними? Чтобы мы вернули соединение с БД в пул, ожидая ответа REST.
Я пробовал установить hibernate.connection.release_mode=AFTER_TRANSACTION, и это вроде как помогает, по крайней мере, у нас нет исключений утечки соединения. Единственная проблема в том, что наши тесты начали показывать следующее:
2018-04-17 15:48:03.438 WARN 94029 --- [ main] o.s.orm.jpa.vendor.HibernateJpaDialect : JDBC Connection to reset not identical to originally prepared Connection - please make sure to use connection release mode ON_CLOSE (the default) and to run against Hibernate 4.2+ (or switch HibernateJpaDialect's prepareConnection flag to false`
Тесты используют внедренный DAO для вставки записей в БД и последующей их проверки через API приложения. Они не аннотированы @Transactional, и список слушателей выглядит так:
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
TransactionDbUnitTestExecutionListener.class
})
Есть идеи, в чем может быть проблема с тестами?
Нет, я не использую txManager в коде. Ни мои bean-компоненты не аннотированы @Transactional, я заканчиваю аннотированием DAO.




В коде
public void doStuff() {
A a = dao.findByWhatever();
if (a.hasProperty()) {
B b = restService.doRemoteRequestWithRetries(); // May take long time
}
a.setProp(b.getSomethig());
dao.save(b);
}
Здесь я вижу три задачи - получение объекта A, подключение к удаленной службе и обновление объекта A. И all these are in same transaction, so the underlying connection will be held till the method is complete.
Таким образом, идея состоит в том, чтобы разделить первую и третью задачи на отдельные транзакции, разрешив разъединение соединения перед вызовом удаленной службы.
По сути, при весенней загрузке вам нужно добавить spring.jpa.open-in-view=false. Это не зарегистрирует OpenEntityManagerInViewInterceptor, и, следовательно, entityManager (в свою очередь соединение) не привязан к текущему потоку / запросу.
Затем разделите три задачи на отдельные методы с помощью @Transactional. Это помогает нам привязать entityManager к области транзакции и освободить соединение в конце метода транзакции.
ПРИМЕЧАНИЕ: И убедитесь, что никакая транзакция не запущена / не выполняется перед вызовом этих методов (например, вызывающий, например, контроллер и т. д.). В противном случае цель будет нарушена, и эти новые методы @Transactional будут работать в той же транзакции, что и раньше.
Таким образом, высокоуровневый подход может выглядеть так:
spring.jpa.open-in-view=false.doStuff на три метода в новом классе обслуживания. Намерение состоит в том, чтобы убедиться, что они используют разные транзакции.
will call A a = dao.findByWhatever (); `.will call rest of the code with JPA merge or hibernate saveOrUpdate on objecta`.Работает как шарм, престиж @ madhusudana-reddy-sunnapu
Ваш
@Serviceили@Controllerаннотирован@Transactional? Вы упомянули, что тестов нет, но где границы транзакций в производственном коде? Надеюсь, вы не управляете транзакциями в коде Java, например. автопроводным биномtxManager.