Автор оригинала: Vlad Mihalcea.
Вступление
В этой статье я покажу вам, как вы можете настроить мониторинг подключений для вашего приложения JPA и Hibernate, независимо от того, используете ли вы Spring или другие платформы приложений.
Мониторинг соединений JPA и гибернации – обнаружение режима автоматической фиксации
По умолчанию JDBC Соединение
работает в режиме автоматической фиксации, и для явного задания границ транзакции необходимо отключить режим автоматической фиксации с помощью метода setAutoCommit
и вызвать фиксацию
или откат
в конце транзакции.
Если вам не удастся явно отключить режим автоматической фиксации на уровне пула соединений, то Hibernate сделает это перед началом транзакции и отменит изменение в конце транзакции. Дополнительные операции сопряжены с накладными расходами, которых вам следует избегать.
Итак, давайте предположим, что вы вызываете метод обслуживания, подобный этому:
PagestorePage = storeService.findAll( PageRequest.of(0, 25, Sort.by("id")) );
С настройками Spring по умолчанию вы не знали бы, что режим автоматической фиксации должен быть отключен и повторно включен Hibernate для каждой выполняемой транзакции.
Однако, начиная с версии 2.3, оптимизатор сохраняемости Hy может обнаруживать эти проблемы.
Итак, при выполнении следующего интеграционного теста:
hypersistenceOptimizer.getEvents().clear(); PagestorePage = storeService.findAll( PageRequest.of(pageNumber, pageSize, Sort.by("id")) ); assertTrue(hypersistenceOptimizer.getEvents().isEmpty());
Мы видим, что тест завершается следующим образом:
SELECT s.id AS id1_4_, s.name AS name2_4_ FROM stores s ORDER BY s.id ASC LIMIT ? -- Hypersistence Optimizer : MINOR - AutoCommittingConnectionEvent -- The JDBC Connection was borrowed in auto-commit mode, -- and Hibernate needs to disable this mode when starting a transaction -- and re-enable it prior to releasing the JDBC Connection back to the pool.
Исправить это очень просто. Просто добавьте следующие две строки в свой файл Spring Boot application.properties
:
spring.datasource.hikari.auto-commit=false spring.jpa.properties.hibernate.connection.provider_disables_autocommit=true
Параметр spring.datasource.hikari.autocommit
указывает Hikari на вызов setAutoCommit(false)
на JDBC Соединение
при добавлении нового соединения в пул.
Соединение в режиме гибернации.свойство конфигурации provider_disables_autocommit сообщает Hibernate, что поставщик подключения отключает режим автоматической фиксации перед передачей подключения к базе данных приложению.
Мониторинг соединений JPA и гибернации – обнаружение соединений без оператора
Еще одна очень трудная для отслеживания проблема заключается в том, что соединение с базой данных извлекается, но инструкция SQL не выполняется.
Например, давайте предположим, что мы создали следующий заполняемость парковки
метод обслуживания:
@Override @Transactional(readOnly = true) public Occupancy parkingOccupancy(Integer storeId) { float occupancyRate = parkingService.parkingOccupancyRate(storeId); Occupancy[] occupancies = Occupancy.values(); float maxAllowedRate = 0; Occupancy occupancy = null; for (int i = 0; i < occupancies.length; i++) { occupancy = occupancies[i]; maxAllowedRate = (i + 1f) / occupancies.length; if(occupancyRate < maxAllowedRate) { return occupancy; } } return Occupancy.FULL; }
услуга парковки.заполняемость парковки ()
– это вызов веб-службы, поэтому нам действительно не нужно обращаться к системе баз данных для получения необходимых данных.
Однако, если мы выполним следующий интеграционный тест:
assertTrue(hypersistenceOptimizer.getEvents().isEmpty()); Occupancy occupancy = storeService.parkingOccupancy(storeId); assertTrue(hypersistenceOptimizer.getEvents().isEmpty());
Оптимизатор гибернации сообщает нам о следующих проблемах:
-- Hypersistence Optimizer : MINOR - AutoCommittingConnectionEvent -- The JDBC Connection was borrowed in auto-commit mode, -- and Hibernate needs to disable this mode when starting a transaction -- and re-enable it prior to releasing the JDBC Connection back to the pool. -- Hypersistence Optimizer : MAJOR -- StatementlessConnectionEvent -- The JDBC Connection was borrowed for [15] ms, but no SQL statement was executed.
Проблема также возникает в режиме автоматической фиксации по умолчанию и может быть визуализирована на следующей диаграмме:
В отсутствие гибернации.подключение.параметр provider_disables_autocommit, Hibernate не знает, был ли отключен режим автоматической фиксации, поэтому ему необходимо проверить его.
Однако для этого ему необходимо установить соединение с базой данных в начале метода @Transactional
, поэтому соединение устанавливается в течение определенного времени без необходимости выполнения какой-либо инструкции SQL.
Исправить это очень просто. Просто удалите аннотацию @Transactional
из всех методов, которые не получают доступ к системе базы данных.
Мониторинг соединений JPA и гибернации – обнаружение сеансов без транзакций
JPA и Hibernate позволяют выполнять запросы, даже если вы не запускали транзакцию, поскольку транзакции необходимы JPA только для записи данных.
Итак, в следующем примере создается EntityManager
и выполняются два запроса:
hypersistenceOptimizer.getEvents().clear(); try(Session entityManager = entityManagerFactory .createEntityManager().unwrap(Session.class)) { Post post = entityManager.createQuery(""" select p from Post p where p.id = :id """, Post.class) .setParameter("id", 1L) .getSingleResult(); int postCount = ((Number) entityManager.createQuery(""" select count(p) from Post p """) .getSingleResult()).intValue(); } assertTrue(hypersistenceOptimizer.getEvents().isEmpty());
Однако при выполнении приведенного выше интеграционного теста мы получаем следующий сбой:
SELECT p.id AS id1_0_, p.title AS title2_0_ FROM post p WHERE p.id = ? SELECT COUNT(p.id) AS col_0_0_ FROM post p -- Hypersistence Optimizer - CRITICAL -- TransactionlessSessionEvent -- The JPA EntityManager or Hibernate Session has acquired [2] database connections -- because the Persistence Context is not transactional. -- You should switch to using a transactional Persistence Context, -- which allows you to execute all SQL statements using a single database connection.
Исправить это очень просто. Просто запустите транзакцию для JPA EntityManager
, и все операторы SQL будут включены в контекст одной и той же транзакции базы данных.
Обо всех обнаруженных нами проблемах сообщил оптимизатор гиперсуществования , который работает с Spring Boot, Spring, Java EE, Jakarta EE, Quarkus, Play, JBoss, Glassfish или другими серверами приложений.
Чтобы включить оптимизатор гиперсуществования, вам необходимо добавить io.гиперсуществование:оптимизатор гиперсуществования:${гиперсуществование-оптимизатор.версия}
Maven зависимость и настроить Гиперсуществующий оптимизатор
компонент:
@Configuration public class HypersistenceConfiguration { @Bean public HypersistenceOptimizer hypersistenceOptimizer( EntityManagerFactory entityManagerFactory) { return new HypersistenceOptimizer( new JpaConfig(entityManagerFactory) ); } }
Вот и все!
Вывод
Мониторинг подключений и транзакций очень важен при реализации уровня доступа к данным, и JPA и Hibernate ничем не отличаются. Благодаря инструменту оптимизатора гиперсистенции вы можете отслеживать проблемы с отображением, настройкой, запросами, сеансами и подключениями прямо из ваших интеграционных тестов, тем самым предотвращая их влияние на производственную систему.