如何在Spring切面中获取执行的JPA查询语句?附环境与代码片段
如何通过Spring切面获取JPA执行的查询语句?
先明确你的环境信息:
- Spring Boot版本:2.0.1.RELEASE
- AspectJ版本:1.8.9
- Hibernate版本:5.2.14.Final
结合你已经写好的JpaInterceptor切面类,我给你两个直接可行的方案,帮你获取执行的JPA查询语句:
方案一:直接拦截Hibernate Query的执行方法
你的JPA底层是Hibernate,所有JPA查询最终都会转化为Hibernate的Query对象执行,直接拦截这个对象的核心执行方法是最精准的方式。完善你的JpaInterceptor类即可:
@Aspect @Configuration public class JpaInterceptor { private static Logger logger = LoggerFactory.getLogger(JpaInterceptor.class); private ThreadLocal<SimpleDateFormat> sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss:SSS]") ); // 定义切点:拦截Hibernate Query的所有核心执行方法 @Pointcut("execution(* org.hibernate.query.Query.list(..)) || " + "execution(* org.hibernate.query.Query.uniqueResult(..)) || " + "execution(* org.hibernate.query.Query.executeUpdate(..)) || " + "execution(* org.hibernate.query.Query.getSingleResult(..))") public void hibernateQueryExecution() {} // 环绕通知:获取查询语句并记录,再执行原方法 @Around("hibernateQueryExecution()") public Object interceptQueryExecution(ProceedingJoinPoint joinPoint) throws Throwable { // 获取当前执行的Hibernate Query对象 org.hibernate.query.Query<?> query = (org.hibernate.query.Query<?>) joinPoint.getTarget(); // 获取原始的HQL/SQL查询语句 String queryString = query.getQueryString(); // 获取查询绑定的命名参数(可选,方便查看完整的查询上下文) Map<String, Object> namedParams = query.getNamedParameters(); // 打印或记录查询日志 logger.info("{} 执行JPA查询:{},参数:{}", sdf.get().format(new Date()), queryString, namedParams); // 继续执行原查询方法 return joinPoint.proceed(); } }
注意事项:
- 你的Hibernate版本是5.2.x,
Query接口已经从org.hibernate包移到了org.hibernate.query包下,所以切点的包路径要写对,不然拦截不到。 - 不管是JPQL查询还是原生SQL查询,都会被这个切面拦截到,因为它们都实现了
org.hibernate.query.Query接口。 - 要确保AspectJ织入生效:在Spring Boot启动类上添加
@EnableAspectJAutoProxy(proxyTargetClass = true),同时pom.xml里要包含AspectJ的依赖:
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.9</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency>
方案二:拦截Spring Data JPA的Repository方法(可选)
如果你的项目用了Spring Data JPA,也可以拦截Repository的方法执行,不过这种方式相对麻烦,不如方案一精准。示例如下:
@Aspect @Configuration public class JpaInterceptor { private static Logger logger = LoggerFactory.getLogger(JpaInterceptor.class); private ThreadLocal<SimpleDateFormat> sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss:SSS]") ); // 定义切点:拦截你所有Repository接口的方法 @Pointcut("execution(* com.yourproject.repository.*.*(..))") public void repositoryMethodExecution() {} @Autowired private EntityManager entityManager; @Around("repositoryMethodExecution()") public Object interceptRepositoryMethod(ProceedingJoinPoint joinPoint) throws Throwable { // 获取Hibernate Session Session session = entityManager.unwrap(Session.class); // 记录方法执行信息(一个Repository方法可能生成多条Query,所以无法直接获取单条查询语句) logger.info("{} 执行Repository方法:{}", sdf.get().format(new Date()), joinPoint.getSignature().getName()); return joinPoint.proceed(); } }
总的来说,方案一的拦截方式更直接,能精准获取每一条执行的JPA查询语句和参数,更适合你的需求。
内容的提问来源于stack exchange,提问作者ashishgupta_mca
相关产品推荐
相关产品推荐

