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

 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.classRepositoryInformation 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// EventPublishingRepositoryProxyPostProcessorpostProcessors.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;// 忽略其他@Overrideprotected 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();// 设置RepositoryAnnotationTransactionAttributeSourcetransactionInterceptor.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的@Transactionalsuper(true);Assert.notNull(repositoryInformation, "RepositoryInformation must not be null");// enableDefaultTransactions默认为truethis.enableDefaultTransactions = enableDefaultTransactions;// DefaultRepositoryInformation对象this.repositoryInformation = repositoryInformation;}@Override@Nullableprotected 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注解。

结尾

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/25529.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker 基础使用 (4) 网络管理

文章目录 Docker 网络管理需求Docker 网络架构认识Docker 常见网络类型1. bridge 网络2. host 网络3. container 网络4. none 网络5. overlay 网络 Docker 网路基础指令Docker 网络管理实操 其他相关链接 Docker 基础使用(0&#xff09;基础认识 Docker 基础使用(1&#xff09;…

文件操作(Python和C++版)

一、C版 程序运行时产生的数据都属于临时数据&#xff0c;程序—旦运行结束都会被释放通过文件可以将数据持久化 C中对文件操作需要包含头文件< fstream > 文件类型分为两种: 1. 文本文件 - 文件以文本的ASCII码形式存储在计算机中 2. 二进制文件- 文件以文本的二进…

Spring运维之boo项目表现层测试匹配响应执行状态响应体JSON和响应头

匹配响应执行状态 我们创建了测试环境 而且发送了虚拟的请求 我们接下来要进行验证 验证请求和预期值是否匹配 MVC结果匹配器 匹配上了 匹配失败 package com.example.demo;import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Auto…

Transformer动画讲解:Softmax函数

暑期实习基本结束了&#xff0c;校招即将开启。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。提前准备才是完全之策。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c…

读AI未来进行式笔记07量子计算

1. AI审讯技术 1.1. 发明者最初的目的是发明一种能够替代精神药物&#xff0c;为人类带来终极快乐的技术 1.1.1. 遗憾的是&#xff0c;他找到的只是通往反方向的大门 1.2. 通过非侵入式的神经电磁干扰大脑边缘系统&#xff0c;诱发受审者最…

VRRP基础配置(华为)

#交换设备 VRRP基础配置 VRRP (Virtual Router Redundancy Protocol) 全称是虚拟路由规元余协议&#xff0c;它是一种容错协议。该协议通过把几台路由设备联合组成一台虚拟的路由设备&#xff0c;该虚拟路由器在本地局域网拥有唯一的一个虚拟 ID 和虚拟 IP 地址。实际上&…

UV胶的均匀性对产品质量有什么影响吗?

UV胶的均匀性对产品质量有什么影响吗? UV胶的均匀性对产品质量具有显著的影响&#xff0c;主要体现在以下几个方面&#xff1a; 粘合强度&#xff1a;UV胶的均匀性直接影响其粘合强度。如果UV胶分布不均匀&#xff0c;可能导致部分区域粘接力不足&#xff0c;从而影响产品的…

报错:CMake Error OpenCVConfig.cmake opencv-config.cmake

1、编译过程中&#xff0c;出现OpenCV 报错问题 报错&#xff1a;CMake Error OpenCVConfig.cmake opencv-config.cmake 解决思路&#xff1a;参考此链接

LangChain + ChatGLM 实现本地知识库问答

基于LangChain ChatGLM 搭建融合本地知识的问答机器人 1 背景介绍 近半年以来&#xff0c;随着ChatGPT的火爆&#xff0c;使得LLM成为研究和应用的热点&#xff0c;但是市面上大部分LLM都存在一个共同的问题&#xff1a;模型都是基于过去的经验数据进行训练完成&#xff0c;无…

Python进阶-部署Flask项目(以TensorFlow图像识别项目WSGI方式启动为例)

本文详细介绍了如何通过WSGI方式部署一个基于TensorFlow图像识别的Flask项目。首先简要介绍了Flask框架的基本概念及其特点&#xff0c;其次详细阐述了Flask项目的部署流程&#xff0c;涵盖了服务器环境配置、Flask应用的创建与测试、WSGI服务器的安装与配置等内容。本文旨在帮…

JAVA-LeetCode 热题 100 第56.合并区间

思路&#xff1a; class Solution {public int[][] merge(int[][] intervals) {if(intervals.length < 1) return intervals;List<int[]> res new ArrayList<>();Arrays.sort(intervals, (o1,o2) -> o1[0] - o2[0]);for(int[] interval : intervals){if(res…

【嵌入式】波特率9600,发送8个字节需要多少时间,如何计算?

问题&#xff1a; 波特率9600&#xff0c;发送 01 03 00 00 00 04 44 09 (8字节) 需要多少时间&#xff0c;如何计算&#xff1f; 在计算发送数据的时间时&#xff0c;首先要考虑波特率以及每个字符的数据格式。对于波特率9600和标准的UART数据格式&#xff08;1个起始位&…

AIRNet模型使用与代码分析(All-In-One Image Restoration Network)

AIRNet提出了一种较为简易的pipeline&#xff0c;以单一网络结构应对多种任务需求&#xff08;不同类型&#xff0c;不同程度&#xff09;。但在效果上看&#xff0c;ALL-In-One是不如One-By-One的&#xff0c;且本文方法的亮点是batch内选择patch进行对比学习。在与sota对比上…

JAVA-学习-2

一、类 1、类的定义 把相似的对象划分了一个类。 类指的就是一种模板&#xff0c;定义了一种特定类型的所有对象的属性和行为 在一个.java的问题件中&#xff0c;可以有多个class&#xff0c;但是智能有一个class是用public的class。被声明的public的class&#xff0c;必须和文…

Pytorch 实现目标检测一(Pytorch 23)

一 目标检测和边界框 在图像分类任务中&#xff0c;我们假设图像中只有一个主要物体对象&#xff0c;我们只关注如何识别其类别。然而&#xff0c;很多时候图像里有多个我们感兴趣的目标&#xff0c;我们不仅想知 道它们的类别&#xff0c;还想得到它们在图像中的具体位置。在…

【前端】响应式布局笔记——自适应布局

自适应布局 自适应布局是不同设备对应不同的html(局部自适应)&#xff0c;通过判断设备的类型或控制局部的变化。 1、获取设备是移动端还是pc端 // 获取设备的信息let userAgent navigator.userAgent.toLowerCase();// 使用正则表达式来判断类型let device /ipad|iphone|m…

数据并非都是正态分布:三种常见的统计分布及其应用

你有没有过这样的经历&#xff1f;使用一款减肥app&#xff0c;通过它的图表来监控自己的体重变化&#xff0c;并预测何时能达到理想体重。这款app预测我需要八年时间才能恢复到大学时的体重&#xff0c;这种不切实际的预测是因为应用使用了简单的线性模型来进行体重预测。这个…

服务部署:.NET项目使用Docker构建镜像与部署

前提条件 安装Docker&#xff1a;确保你的Linux系统上已经安装了Docker。如果没有&#xff0c;请参考官方文档进行安装。 步骤一&#xff1a;准备项目文件 将你的.NET项目从Windows系统复制到Linux系统。你可以使用Git、SCP等工具来完成这个操作。如何是使用virtualbox虚拟电…

国产操作系统上给virtualbox中win7虚拟机安装增强工具 _ 统信 _ 麒麟 _ 中科方德

原文链接&#xff1a;国产操作系统上给virtualbox中win7虚拟机安装增强工具 | 统信 | 麒麟 | 中科方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇在国产操作系统上给win7虚拟机安装virtualbox增强工具的文章。VirtualBox增强工具&#xff08;Guest Additions&a…

Liunx环境下redis主从集群搭建(保姆级教学)02

Redis在linux下的主从集群配置 本次演示使用三个节点实例一个主节点&#xff0c;两个从节点&#xff1a;7000端口&#xff08;主&#xff09;&#xff0c;7001端口&#xff08;从&#xff09;&#xff0c;7002端口&#xff08;从&#xff09;&#xff1b; 主节点负责写数据&a…