You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:00:18