首页 > 编程语言 >【源码】Spring Data JPA原理解析之事务注册原理

【源码】Spring Data JPA原理解析之事务注册原理

时间:2024-06-07 17:33:43浏览次数:19  
标签:Repository JPA Spring 源码 原理 null Data

 Spring Data JPA系列

1、SpringBoot集成JPA及基本使用

2、Spring Data JPA Criteria查询、部分字段查询

3、Spring Data JPA数据批量插入、批量更新真的用对了吗

4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作

5、Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用

6、【源码】Spring Data JPA原理解析之Repository的自动注入(一)

7、【源码】Spring Data JPA原理解析之Repository的自动注入(二)

8、【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码

9、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(一)

10、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)

11、【源码】Spring Data JPA原理解析之Repository自定义方法添加@Query注解的执行原理

12、【源码】SpringBoot事务注册原理

13、【源码】Spring Data JPA原理解析之事务注册原理

14、【源码】Spring Data JPA原理解析之事务执行原理

前言

JPA是Java Persistence API的简称,中文名Java持久层API。JPA采用ORM对象关系映射,以Java面向对象的编程思想,在javax.persistence包下提供对实体对象的CRUD操作,将开发者从繁琐的JDBC和SQL代码中解脱出来。

在数据库的操作中,为了处理脏读、不可重复读、幻读等问题,需要通过事务来处理。本篇从源码的角度和大家一下分享一下Spring Data JPA中的Repository的事务注册原理。

Repository的事务注册

在Spring框架中,数据操作的事务是通过添加@Transaction注解来实现的。详见:

【源码】SpringBoot事务注册原理-CSDN博客

在Spring Data JPA中,实现事务也是只需要在对应的方法中添加@Transaction注解即可。下面从源码的角度来分析一下事务实现的原理。

【源码】Spring Data JPA原理解析之Repository的自动注入(二)-CSDN博客

在上面的博文中分享了Repository bean的创建。Respository的bean是一个通过ProxyFactory创建的动态代理对象。源码如下:

public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, BeanFactoryAware {
    /**
	 * 返回给定接口的存储库实例,该实例由为自定义逻辑提供实现逻辑的实例支持。
	 */
	@SuppressWarnings({ "unchecked" })
	public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
 
		if (logger.isDebugEnabled()) {
			logger.debug(LogMessage.format("Initializing repository instance for %s…", repositoryInterface.getName()));
		}
 
		Assert.notNull(repositoryInterface, "Repository interface must not be null");
		Assert.notNull(fragments, "RepositoryFragments must not be null");
 
		ApplicationStartup applicationStartup = getStartup();
 
		StartupStep repositoryInit = onEvent(applicationStartup, "spring.data.repository.init", repositoryInterface);
 
		repositoryBaseClass.ifPresent(it -> repositoryInit.tag("baseClass", it.getName()));
 
		StartupStep repositoryMetadataStep = onEvent(applicationStartup, "spring.data.repository.metadata",
				repositoryInterface);
		// 获取repository元数据,包括Repository<T, ID>中的T类型、ID类型、接口类型(如GoodsRepository)等
		RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
		repositoryMetadataStep.end();
 
		StartupStep repositoryCompositionStep = onEvent(applicationStartup, "spring.data.repository.composition",
				repositoryInterface);
		repositoryCompositionStep.tag("fragment.count", String.valueOf(fragments.size()));
		// 获取
		RepositoryComposition composition = getRepositoryComposition(metadata, fragments);
		// 获取Repository信息,getRepositoryInformation()返回一个RepositoryInformation对象。
		// 如子类JpaRepositoryFactory,指定baseClass为SimpleJpaRepository.class
		RepositoryInformation information = getRepositoryInformation(metadata, composition);
 
		repositoryCompositionStep.tag("fragments", () -> {
 
			StringBuilder fragmentsTag = new StringBuilder();
 
			for (RepositoryFragment<?> fragment : composition.getFragments()) {
 
				if (fragmentsTag.length() > 0) {
					fragmentsTag.append(";");
				}
 
				fragmentsTag.append(fragment.getSignatureContributor().getName());
				fragmentsTag.append(fragment.getImplementation().map(it -> ":" + it.getClass().getName()).orElse(""));
			}
 
			return fragmentsTag.toString();
		});
 
		repositoryCompositionStep.end();
 
		StartupStep repositoryTargetStep = onEvent(applicationStartup, "spring.data.repository.target",
				repositoryInterface);
		// 获取目标Repository对象,SimpleJpaRepository对象
		Object target = getTargetRepository(information);
 
		repositoryTargetStep.tag("target", target.getClass().getName());
		repositoryTargetStep.end();
 
		RepositoryComposition compositionToUse = composition.append(RepositoryFragment.implemented(target));
		validate(information, compositionToUse);
 
		// Create proxy
		// 创建代理对象
		StartupStep repositoryProxyStep = onEvent(applicationStartup, "spring.data.repository.proxy", repositoryInterface);
		ProxyFactory result = new ProxyFactory();
		result.setTarget(target);
		// 代理对象实现的接口
		result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);
 
		if (MethodInvocationValidator.supports(repositoryInterface)) {
			result.addAdvice(new MethodInvocationValidator());
		}
		// 添加界面
		result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
 
		if (!postProcessors.isEmpty()) {
			StartupStep repositoryPostprocessorsStep = onEvent(applicationStartup, "spring.data.repository.postprocessors",
					repositoryInterface);
			// 执行后置处理
			// CrudMethodMetadataPostProcessor
			// TransactionalRepositoryProxyPostProcessor
			// JpaRepositoryFactory构造方法中加入的内部处理器,
			// 添加SurroundingTransactionDetectorMethodInterceptor,记录是否处理状态
			// PersistenceExceptionTranslationRepositoryProxyPostProcessor
			// EventPublishingRepositoryProxyPostProcessor
			postProcessors.forEach(processor -> {
 
				StartupStep singlePostProcessor = onEvent(applicationStartup, "spring.data.repository.postprocessor",
						repositoryInterface);
				singlePostProcessor.tag("type", processor.getClass().getName());
				processor.postProcess(result, information);
				singlePostProcessor.end();
			});
			repositoryPostprocessorsStep.end();
		}
 
		if (DefaultMethodInvokingMethodInterceptor.hasDefaultMethods(repositoryInterface)) {
			// 添加DefaultMethodInvokingMethodInterceptor拦截器
			result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
		}
 
		Optional<QueryLookupStrategy> queryLookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
				evaluationContextProvider);
		// 添加QueryExecutorMethodInterceptor拦截器
		result.addAdvice(new QueryExecutorMethodInterceptor(information, getProjectionFactory(), queryLookupStrategy,
				namedQueries, queryPostProcessors, methodInvocationListeners));
		// 添加ImplementationMethodExecutionInterceptor拦截器
		result.addAdvice(
				new ImplementationMethodExecutionInterceptor(information, compositionToUse, methodInvocationListeners));
 
		T repository = (T) result.getProxy(classLoader);
		repositoryProxyStep.end();
		repositoryInit.end();
 
		if (logger.isDebugEnabled()) {
			logger
					.debug(LogMessage.format("Finished creation of repository instance for %s.",
				repositoryInterface.getName()));
		}
 
		return repository;
	}
}

在生成代理对象之前,如果有后置处理器postProcessors,则执行后置处理器的postProcess()方法。在后置处理器中,有一个TransactionalRepositoryProxyPostProcessor。

JpaRepositoryFactoryBean

【源码】Spring Data JPA原理解析之Repository的自动注入(一)-CSDN博客

在上面的博文中分享了JpaRepositoriesAutoConfiguration的源码,在Spring Data JPA自动注入时,会自动注入JpaRepositoryConfigExtension,在JpaRepositoryConfigExtension中getRepositoryFactoryBeanClassName()方法,返回JpaRepositoryFactoryBean.class.getName(),该类最终会被装载到Spring IOC容器中。

JpaRepositoryFactoryBean初始化时,执行父父类RepositoryFactoryBeanSupport的afterPropertiesSet()方法,在该方法中,主要执行如下:

3.1)调用抽象方法createRepositoryFactory()创建Repository工厂对象,在TransactionalRepositoryFactoryBeanSupport中实现了该方法;

在createRepositoryFactory()方法中,创建了Repository工厂之后,添加了两个后置处理器,其中一个为TransactionalRepositoryProxyPostProcessor。

3.2)为3.1)中的Repository工厂对象设置查询查找策略、NamedQueries、后置处理器等;

3.3)调用3.1)中的Repository工厂对象,调用getRepository()创建代理的Repository对象;

详细可以看【源码】Spring Data JPA原理解析之Repository的自动注入(二)

TransactionalRepositoryFactoryBeanSupport

TransactionalRepositoryFactoryBeanSupport的源码如下:

package org.springframework.data.repository.core.support;

public abstract class TransactionalRepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID>
		extends RepositoryFactoryBeanSupport<T, S, ID> implements BeanFactoryAware {

	private String transactionManagerName = TxUtils.DEFAULT_TRANSACTION_MANAGER;
	private @Nullable RepositoryProxyPostProcessor txPostProcessor;
	private @Nullable RepositoryProxyPostProcessor exceptionPostProcessor;
	private boolean enableDefaultTransactions = true;
	
	// 忽略其他

	@Override
	protected final RepositoryFactorySupport createRepositoryFactory() {
		// 调用抽象方法,创建一个RepositoryFactorySupport对象,JpaRepositoryFactory对象
		RepositoryFactorySupport factory = doCreateRepositoryFactory();
		// 添加两个后置处理器,分别为PersistenceExceptionTranslationRepositoryProxyPostProcessor
		// 和TransactionalRepositoryProxyPostProcessor。在setBeanFactory()方法中初始化
		RepositoryProxyPostProcessor exceptionPostProcessor = this.exceptionPostProcessor;

		if (exceptionPostProcessor != null) {
			factory.addRepositoryProxyPostProcessor(exceptionPostProcessor);
		}

		RepositoryProxyPostProcessor txPostProcessor = this.txPostProcessor;

		if (txPostProcessor != null) {
			factory.addRepositoryProxyPostProcessor(txPostProcessor);
		}

		return factory;
	}

	protected abstract RepositoryFactorySupport doCreateRepositoryFactory();

	public void setBeanFactory(BeanFactory beanFactory) {

		Assert.isInstanceOf(ListableBeanFactory.class, beanFactory);

		super.setBeanFactory(beanFactory);
		// 创建后置处理器
		ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
		this.txPostProcessor = new TransactionalRepositoryProxyPostProcessor(listableBeanFactory, transactionManagerName,
				enableDefaultTransactions);
		this.exceptionPostProcessor = new PersistenceExceptionTranslationRepositoryProxyPostProcessor(listableBeanFactory);
	}
}

1)TransactionalRepositoryFactoryBeanSupport实现了BeanFactoryAware,在初始化前,会执行setBeanFactory()方法,在该方法中,创建了两个后置处理器,分别为PersistenceExceptionTranslationRepositoryProxyPostProcessor和TransactionalRepositoryProxyPostProcessor;

2)在createRepositoryFactory()方法中,执行子类JpaRepositoryFactoryBean的doCreateRepositoryFactory()方法,创建JpaRepositoryFactory对象。并添加了1)中创建的两个后置处理器,其中一个为TransactionalRepositoryProxyPostProcessor;

TransactionalRepositoryProxyPostProcessor

在TransactionalRepositoryProxyPostProcessor处理器中,会向代理工厂添加TransactionInterceptor拦截器。

TransactionalRepositoryProxyPostProcessor的代码如下:

package org.springframework.data.repository.core.support;

class TransactionalRepositoryProxyPostProcessor implements RepositoryProxyPostProcessor {

	private final BeanFactory beanFactory;
	private final String transactionManagerName;
	private final boolean enableDefaultTransactions;

	public TransactionalRepositoryProxyPostProcessor(ListableBeanFactory beanFactory, String transactionManagerName,
			boolean enableDefaultTransaction) {

		Assert.notNull(beanFactory, "BeanFactory must not be null");
		Assert.notNull(transactionManagerName, "TransactionManagerName must not be null");

		this.beanFactory = beanFactory;
		this.transactionManagerName = transactionManagerName;
		this.enableDefaultTransactions = enableDefaultTransaction;
	}

	public void postProcess(ProxyFactory factory, RepositoryInformation repositoryInformation) {
		// 定义一个TransactionInterceptor对象
		TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
		// 设置RepositoryAnnotationTransactionAttributeSource
		transactionInterceptor.setTransactionAttributeSource(
				new RepositoryAnnotationTransactionAttributeSource(repositoryInformation, enableDefaultTransactions));
		transactionInterceptor.setTransactionManagerBeanName(transactionManagerName);
		transactionInterceptor.setBeanFactory(beanFactory);
		transactionInterceptor.afterPropertiesSet();
		// 添加TransactionInterceptor拦截器
		factory.addAdvice(transactionInterceptor);
	}

	static class RepositoryAnnotationTransactionAttributeSource extends AnnotationTransactionAttributeSource {

		private static final long serialVersionUID = 7229616838812819438L;

		private final RepositoryInformation repositoryInformation;
		private final boolean enableDefaultTransactions;

		public RepositoryAnnotationTransactionAttributeSource(RepositoryInformation repositoryInformation,
				boolean enableDefaultTransactions) {
			// 执行父类AnnotationTransactionAttributeSource,事务的分析器
			// 为JtaTransactionAnnotationParser和SpringTransactionAnnotationParser。
			// 即支持javax.transaction.Transactional的@Transactional注解和spring的@Transactional
			super(true);

			Assert.notNull(repositoryInformation, "RepositoryInformation must not be null");
			// enableDefaultTransactions默认为true
			this.enableDefaultTransactions = enableDefaultTransactions;
			// DefaultRepositoryInformation对象
			this.repositoryInformation = repositoryInformation;
		}

		@Override
		@Nullable
		protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {

			// Don't allow no-public methods as required.
			if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
				return null;
			}

			// Ignore CGLIB subclasses - introspect the actual user class.
			Class<?> userClass = targetClass == null ? targetClass : ProxyUtils.getUserClass(targetClass);

			// The method may be on an interface, but we need attributes from the target class.
			// If the target class is null, the method will be unchanged.
			Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);

			// If we are dealing with method with generic parameters, find the original method.
			// 如果正在处理具有泛型参数的方法,请找到原始方法。
			specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

			TransactionAttribute txAtt = null;

			if (specificMethod != method) {

				// Fallback is to look at the original method.
				txAtt = findTransactionAttribute(method);

				if (txAtt != null) {
					return txAtt;
				}

				// Last fallback is the class of the original method.
				txAtt = findTransactionAttribute(method.getDeclaringClass());

				if (txAtt != null || !enableDefaultTransactions) {
					return txAtt;
				}
			}

			// First try is the method in the target class.
			txAtt = findTransactionAttribute(specificMethod);

			if (txAtt != null) {
				return txAtt;
			}

			// Second try is the transaction attribute on the target class.
			txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());

			if (txAtt != null) {
				return txAtt;
			}

			if (!enableDefaultTransactions) {
				return null;
			}

			// Fallback to implementation class transaction settings of nothing found
			// return findTransactionAttribute(method);
			Method targetClassMethod = repositoryInformation.getTargetClassMethod(method);

			if (targetClassMethod.equals(method)) {
				return null;
			}

			txAtt = findTransactionAttribute(targetClassMethod);

			if (txAtt != null) {
				return txAtt;
			}

			txAtt = findTransactionAttribute(targetClassMethod.getDeclaringClass());

			if (txAtt != null) {
				return txAtt;
			}

			return null;
		}
	}
}

5.1 在postProcess()方法中,执行如下:

1)声明一个TransactionInterceptor对象;

2)声明一个RepositoryAnnotationTransactionAttributeSource对象;

3)添加transactionManagerName和beanFactory;

4)在ProxyFactory添加TransactionInterceptor拦截器;

5.2 RepositoryAnnotationTransactionAttributeSource注解事务属性资源类为内部类,该类继承AnnotationTransactionAttributeSource。RepositoryAnnotationTransactionAttributeSource的构造方法中,执行父类的构造方法。

在AnnotationTransactionAttributeSource的构造方法中,会添加注解事务的解析器。添加了SpringTransactionAnnotationParser和JtaTransactionAnnotationParser。分别用于解析org.springframework.transaction.annotation包和javax.transaction包下的@Transactional注解。即在代码中使用两个包的@Transactional注解都可以实现事务。

在RepositoryAnnotationTransactionAttributeSource的computeTransactionAttribute()方法中,会调用父类AnnotationTransactionAttributeSource的findTransactionAttribute(),进而执行determineTransactionAttribute()方法,遍历解析器,解析对应的@Transactional注解。

结尾

限于篇幅,本篇先分享到这里。

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

标签:Repository,JPA,Spring,源码,原理,null,Data
From: https://blog.csdn.net/JingAi_jia917/article/details/139443219

相关文章

  • 【计算机毕业设计】基于SSM++jsp的在线医疗服务系统【源码+lw+部署文档】
    包含论文源码的压缩包较大,请私信或者加我的绿色小软件获取免责声明:资料部分来源于合法的互联网渠道收集和整理,部分自己学习积累成果,供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。本人尊重原创作者或出版方,资料版权归原作者或出版方所有,本人不对所......
  • 【计算机毕业设计】基于SSM++jsp的医院门诊挂号系统【源码+lw+部署文档】
    包含论文源码的压缩包较大,请私信或者加我的绿色小软件获取免责声明:资料部分来源于合法的互联网渠道收集和整理,部分自己学习积累成果,供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。本人尊重原创作者或出版方,资料版权归原作者或出版方所有,本人不对所......
  • 讲清楚 Elasticsearch scroll 的底层原理
    Elasticsearch的Scroll主要用于高效地分批检索大量数据记录,适用于那些数据量过大而不能一次性通过标准搜索请求获取所有结果的场景。Scroll机制的工作原理类似于数据库中的游标(cursor),它允许用户发起一次搜索请求后,通过维护一个持续的上下文(context)来分批次获取所有匹配的文档,......
  • 基于SpringBoot+Vue的网上花店系统设计与实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • 基于SpringBoot+Vue的校园驿站管理系统设计与实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • 基于SpringBoot+Vue的二手手机交易平台的详细设计和实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • pr的工作原理
    PremierePro了解pr面板菜单栏:里面包括文件,编辑,剪辑,序列,标记,窗口,帮助等,在这些菜单里还有子菜单。效果面板:双击素材,可以在效果面板里进行编辑你想要的效果。节目面板:在时间轴里编辑时,要是查看效果在节目面板里进行查看。项目面板:导入和新建的素材都可以在这里面进行管理,也可以把素......
  • Chroium 源码目录结构分析(3):目录和模块总结(src根目录部分)
    上一篇,我们通过脚本对主要的两个目录进行信息统计和提取,由于提取的内容过多不便于直接阅读,因此我们使用AI帮我们总结。提示词:你是chromium项目的专家,专业的软件工程师。这分文档是google的chromium开源项目的目录结构和对应目录模块的readme的摘要,请你根据文档内容,依次总......
  • pr工作原理文档
    AdobePremierePro的工作原理思维导图Pr面板展示图第一步:导入视频1导入点击文件——点击导入——选择视频,双击视频或点击打开按钮——完成导入。2预览点击视频,拖到右侧;拖动蓝色条,时间指针,点击会快速预览,快捷键是空格键。3视频轨道和音频轨道A声音;V:视频;左右拖动控制播放时间;第二......
  • pr工作原理
    Pr图像式基础课一新建项目二新建序列下一页看后续三颜色调出Lumetri颜色,色轮部分就可以进行颜色的调整(个人)四认识三面板与效果效果控件是pr软件的重要组成部分,它可以对视频效果的调节时间轴上可以进行调节与裁剪无用的视频段画面的展示和查看搜索查找效果五工具的认识选择工具(对时......