# spring-hibernate-query-utils **Repository Path**: mirrors_andyglick/spring-hibernate-query-utils ## Basic Information - **Project Name**: spring-hibernate-query-utils - **Description**: Library giving tools to detect N+1 queries and count the queries generated with Spring and Hibernate - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-09-24 - **Last Updated**: 2026-02-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [![Contributors][contributors-shield]][contributors-url] [![Forks][forks-shield]][forks-url] [![Stargazers][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] [![MIT License][license-shield]][license-url]

Spring Hibernate Query Utils

No more N+1 queries in your Spring applications
Spring Hibernate Query Utils: an easy way of detecting N+1 queries and counting queries in a Spring/Hibernate application

Report Bug ยท Request Feature

## Table of Contents * [About the Project](#about-the-project) * [Getting Started](#getting-started) * [Prerequisites](#prerequisites) * [Installation](#installation) * [Usage](#usage) * [N+1 Query Detection](#n1-query-detection) * [Detection](#detection) * [Configuration](#configuration) * [Query Count](#query-count) * [License](#license) * [Contact](#contact) ## About The Project While investigating the performance problems in my SpringBoot application, I discovered the infamous N+1 queries problem (more details on this problem [here](https://medium.com/@mansoor_ali/hibernate-n-1-queries-problem-8a926b69f618)) that was killing the performance of my services. After managing to fix this problem, I had to find a way to detect it and raise the alarm to avoid any developer to introduce new ones. That is why I created Spring Hibernate Query Utils to provide an easy way of detecting N+1 queries and counting the queries generated in a Spring application using Hibernate. If you develop Spring applications using Hibernate, you have probably also encountered performance issues caused by N+1 queries. This library provides several benefits: * Kill the N+1 queries problem by throwing an exception when detecting it in your tests * Count the exact number of queries generated for each service or resource * Improve the onboarding of new developers by making them understand the impact of the N+1 queries problem * Improve the debugging by seeing which query is executed and when ## Getting Started ### Prerequisites * JDK 8 or more. ### Installation ##### Maven Add the dependency to your project inside your `pom.xml` file ```xml com.yannbriancon spring-hibernate-query-utils 1.0.1 ``` ## Usage ### N+1 Query Detection #### Detection The N+1 query detection is enabled by default so no configuration is needed. Each time a N+1 query is detected in a transaction, a log of level error will be sent. Here is an example catching the error log: ```java @RunWith(MockitoJUnitRunner.class) @SpringBootTest @Transactional class NPlusOneQueryLoggingTest { @Autowired private MessageRepository messageRepository; @Mock private Appender mockedAppender; @Captor private ArgumentCaptor loggingEventCaptor; @BeforeEach public void setup() { Logger root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); root.addAppender(mockedAppender); } @Test void nPlusOneQueryDetection_isLoggingWhenDetectingNPlusOneQuery() { // Fetch the 2 messages without the authors List messages = messageRepository.findAll(); // Trigger N+1 query List names = messages.stream() .map(message -> message.getAuthor().getName()) .collect(Collectors.toList()); verify(mockedAppender, times(2)).doAppend(loggingEventCaptor.capture()); LoggingEvent loggingEvent = loggingEventCaptor.getAllValues().get(0); assertThat("N+1 query detected for entity: com.yannbriancon.utils.entity.User") .isEqualTo(loggingEvent.getMessage()); assertThat(Level.ERROR).isEqualTo(loggingEvent.getLevel()); } } ``` #### Configuration By default the detection of a N+1 query logs an error to avoid breaking your code. However, my advise is to override the default error level to throw exceptions for your test profile. Now you will easily detect which tests are failing and be able to flag them and set the error level to error logs only on those tests while you are fixing them. To do this, you can configure the error level when a N+1 query is detected using the property `hibernate.query.interceptor.error-level`. 4 levels are available to handle the detection of N+1 queries: * **INFO**: Log a message of level info * **WARN**: Log a message of level warn * **ERROR** (default): Log a message of level error * **EXCEPTION**: Throw a NPlusOneQueryException Here are two examples on how to use it globally or for a specific test: * In application.properties: ```yaml hibernate.query.interceptor.error-level=INFO ``` * In tests: ```java @SpringBootTest("hibernate.query.interceptor.error-level=INFO") @Transactional class NPlusOneQueryLoggingTest { ... } ``` ### Query Count To start counting the generated queries, you need to instantiate a **HibernateQueryInterceptor**. Three methods are available: * startQueryCount: Initializes the query count to 0 and allows the queries to increment the count. * getQueryCount: Returns the current query count for the Thread concerned as a Long. The count is local to a Thread. This choice was made to have a consistent count for a running application and avoid other threads to alter the count. Example in a test: ```java ... import com.yannbriancon.interceptor.HibernateQueryInterceptor; @RunWith(SpringRunner.class) @SpringBootTest @Transactional public class NotificationResourceIntTest { @Autowired private HibernateQueryInterceptor hibernateQueryInterceptor; @Test public void saveFile_isOk() throws Exception { // Initialize the query to 0 and allow the counting hibernateQueryInterceptor.startQueryCount(); // Call the resource that we want to test MvcResult result = mvc.perform(get("/rest/notifications")) .andExpect(status().isOk()) .andReturn(); // Get the query count for this thread and check that it is equal to the number of query you expect, let's say 4. // The count is checked and we detect potential n+1 queries. Assertions.assertThat(hibernateQueryInterceptor.getQueryCount()).isEqualTo(4); } } ``` ## License Distributed under the MIT License. See [`LICENSE`][license-url] for more information. ## Contact [@YBriancon](https://twitter.com/YBriancon) - yann.briancon.73@gmail.com Project Link: [https://github.com/yannbriancon/spring-hibernate-query-count](https://github.com/yannbriancon/spring-hibernate-query-count) [contributors-shield]: https://img.shields.io/github/contributors/yannbriancon/spring-hibernate-query-count.svg?style=flat-square [contributors-url]: https://github.com/yannbriancon/spring-hibernate-query-count/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/yannbriancon/spring-hibernate-query-count.svg?style=flat-square [forks-url]: https://github.com/yannbriancon/spring-hibernate-query-count/network/members [stars-shield]: https://img.shields.io/github/stars/yannbriancon/spring-hibernate-query-count.svg?style=flat-square [stars-url]: https://github.com/yannbriancon/spring-hibernate-query-count/stargazers [issues-shield]: https://img.shields.io/github/issues/yannbriancon/spring-hibernate-query-count.svg?style=flat-square [issues-url]: https://github.com/yannbriancon/spring-hibernate-query-count/issues [license-shield]: https://img.shields.io/github/license/yannbriancon/spring-hibernate-query-count.svg?style=flat-square [license-url]: https://github.com/yannbriancon/spring-hibernate-query-count/blob/master/LICENSE