Рубрики
Без рубрики

Мониторинг соединений с помощью JPA и гибернации

Узнайте, как настроить мониторинг подключений для приложений JPA и гибернации, независимо от того, используете ли вы Spring или другие платформы.

Автор оригинала: Vlad Mihalcea.

Вступление

В этой статье я покажу вам, как вы можете настроить мониторинг подключений для вашего приложения JPA и Hibernate, независимо от того, используете ли вы Spring или другие платформы приложений.

Мониторинг соединений JPA и гибернации – обнаружение режима автоматической фиксации

По умолчанию JDBC Соединение работает в режиме автоматической фиксации, и для явного задания границ транзакции необходимо отключить режим автоматической фиксации с помощью метода setAutoCommit и вызвать фиксацию или откат в конце транзакции.

Если вам не удастся явно отключить режим автоматической фиксации на уровне пула соединений, то Hibernate сделает это перед началом транзакции и отменит изменение в конце транзакции. Дополнительные операции сопряжены с накладными расходами, которых вам следует избегать.

Итак, давайте предположим, что вы вызываете метод обслуживания, подобный этому:

Page storePage = storeService.findAll(
    PageRequest.of(0, 25, Sort.by("id"))
);

С настройками Spring по умолчанию вы не знали бы, что режим автоматической фиксации должен быть отключен и повторно включен Hibernate для каждой выполняемой транзакции.

Однако, начиная с версии 2.3, оптимизатор сохраняемости Hy может обнаруживать эти проблемы.

Итак, при выполнении следующего интеграционного теста:

hypersistenceOptimizer.getEvents().clear();

Page storePage = 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 ничем не отличаются. Благодаря инструменту оптимизатора гиперсистенции вы можете отслеживать проблемы с отображением, настройкой, запросами, сеансами и подключениями прямо из ваших интеграционных тестов, тем самым предотвращая их влияние на производственную систему.