UserRepo 只是个接口,运行时是谁在干活?一次 findAll 经过了哪些拦截器?事务是从哪来的?为什么派生查询没有事务?这篇在 Spring Boot 4.0.8、Hibernate 7、MySQL 上把代理拆开看。
repo.getClass() = jdk.proxy2.$Proxy107;AopUtils.isJdkDynamicProxy = true 实现的接口:[UserRepo, Repository, TransactionalProxy, Advised, DecoratingProxy] 代理的目标对象(Advised.getTargetSource().getTarget()):org.springframework.data.jpa.repository.support.SimpleJpaRepository BeanDefinition 的类:org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean 容器里 UserRepo 类型的 Bean 个数:1;FactoryBean 的 Bean:[&userRepo]
JDK 动态代理,工厂 Bean 是 JpaRepositoryFactoryBean,目标是 SimpleJpaRepository。
…data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor …dao.support.PersistenceExceptionTranslationInterceptor …transaction.interceptor.TransactionInterceptor …data.projection.DefaultMethodInvokingMethodInterceptor …data.repository.core.support.QueryExecutorMethodInterceptor …data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor …dao.support.PersistenceExceptionTranslationInterceptor
7 个拦截器,事务拦截器第 3 个,后面分派到查询方法或基础实现。
findAll()(继承自 CrudRepository):
SQL 发出时:[select] 事务激活=true,只读=true
调用栈里的关键帧(从里到外):[SimpleJpaRepository.findAll, RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0, RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke, QueryExecutorMethodInterceptor.invoke, DefaultMethodInvokingMethodInterceptor.invoke, TransactionInterceptor$1.proceedWithInvocation, TransactionInterceptor.invoke, JdkDynamicAopProxy.invoke, $Proxy107.findAll]
findById(1L):
SQL 发出时:[select] 事务激活=true,只读=true
调用栈里的关键帧(从里到外):[$Proxy98.find, SimpleJpaRepository.findById, RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0, RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke, QueryExecutorMethodInterceptor.invoke, DefaultMethodInvokingMethodInterceptor.invoke, TransactionInterceptor$1.proceedWithInvocation, TransactionInterceptor.invoke, JdkDynamicAopProxy.invoke, $Proxy107.findById]
save(new User):
SQL 发出时:[insert] 事务激活=true,只读=false
调用栈里的关键帧(从里到外):[$Proxy98.persist, SimpleJpaRepository.save, RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0, RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke, QueryExecutorMethodInterceptor.invoke, DefaultMethodInvokingMethodInterceptor.invoke, TransactionInterceptor$1.proceedWithInvocation, TransactionInterceptor.invoke, JdkDynamicAopProxy.invoke, $Proxy107.save]
findByName("a")(派生查询):
SQL 发出时:[select] 事务激活=false,只读=false
调用栈里的关键帧(从里到外):[$Proxy111.getResultList, AbstractJpaQuery.execute, QueryExecutorMethodInterceptor.invoke, DefaultMethodInvokingMethodInterceptor.invoke, TransactionInterceptor$1.proceedWithInvocation, TransactionInterceptor.invoke, JdkDynamicAopProxy.invoke, $Proxy107.findByName]
count():
SQL 发出时:[select] 事务激活=true,只读=true
调用栈里的关键帧(从里到外):[SimpleJpaRepository.count, RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0, RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke, QueryExecutorMethodInterceptor.invoke, DefaultMethodInvokingMethodInterceptor.invoke, TransactionInterceptor$1.proceedWithInvocation, TransactionInterceptor.invoke, JdkDynamicAopProxy.invoke, $Proxy107.count]
custom()(自定义片段 UserRepoCustomImpl) → 返回 来自 UserRepoCustomImpl:
没有发出 SQL
自定义方法体里的事务状态:custom → 事务激活=false,只读=false
hello()(接口 default 方法,里面调 count()) → 返回 default 方法:3:
SQL 发出时:[select] 事务激活=true,只读=true
调用栈里的关键帧(从里到外):[SimpleJpaRepository.count, RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0, RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke, QueryExecutorMethodInterceptor.invoke, DefaultMethodInvokingMethodInterceptor.invoke, TransactionInterceptor$1.proceedWithInvocation, TransactionInterceptor.invoke, JdkDynamicAopProxy.invoke, $Proxy107.count, $Proxy107.hello]来自 SimpleJpaRepository 上的 @Transactional;派生查询和自定义片段没有事务。
外面(调用方)当前:事务激活=false,只读=false
调用方没有事务时,各个 Repository 调用各开各的事务。