diff --git a/README.md b/README.md index 2031d03..f9d3d7b 100644 --- a/README.md +++ b/README.md @@ -279,6 +279,19 @@ public class NotificationResourceIntTest { } ``` +### Reset Query Detection State on each Request +SpringBoot/Tomcat is reusing executer threads. This can lead to wrong N+1 detection due to old queries in ThreadLocal. +Register following interceptor in you WebMvcConfigurer: + +```java +@Override +public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(applicationContext.getBean(HibernateQueryOnRequestResetInterceptor.class)); +} +``` + + + ## Changelog diff --git a/pom.xml b/pom.xml index 5c5caa8..a1d03d7 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.yannbriancon spring-hibernate-query-utils - 2.0.0 + 2.1.0 jar spring-hibernate-query-utils @@ -57,6 +57,11 @@ spring-boot-configuration-processor + + org.springframework.boot + spring-boot-starter-web + + org.springframework.boot spring-boot-starter-validation diff --git a/src/main/java/com/yannbriancon/interceptor/HibernateQueryInterceptor.java b/src/main/java/com/yannbriancon/interceptor/HibernateQueryInterceptor.java index fa4ffb0..d1cc58c 100644 --- a/src/main/java/com/yannbriancon/interceptor/HibernateQueryInterceptor.java +++ b/src/main/java/com/yannbriancon/interceptor/HibernateQueryInterceptor.java @@ -46,7 +46,7 @@ public HibernateQueryInterceptor( /** * Reset the N+1 query detection state */ - private void resetNPlusOneQueryDetectionState() { + public void resetNPlusOneQueryDetectionState() { threadPreviouslyLoadedEntities.set(new HashSet<>()); threadSelectQueriesInfoPerProxyMethod.set(new HashMap<>()); } diff --git a/src/main/java/com/yannbriancon/interceptor/HibernateQueryOnRequestResetInterceptor.java b/src/main/java/com/yannbriancon/interceptor/HibernateQueryOnRequestResetInterceptor.java new file mode 100644 index 0000000..b8a1bfa --- /dev/null +++ b/src/main/java/com/yannbriancon/interceptor/HibernateQueryOnRequestResetInterceptor.java @@ -0,0 +1,25 @@ +package com.yannbriancon.interceptor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Component +@ComponentScan(basePackages = {"com.yannbriancon"}) +public class HibernateQueryOnRequestResetInterceptor implements HandlerInterceptor { + + @Autowired + HibernateQueryInterceptor hibernateQueryInterceptor; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + + // Reset query detection state on each new request + hibernateQueryInterceptor.resetNPlusOneQueryDetectionState(); + return true; + } +}