【源码】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原理解析之事务注册原理

前言

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注解。

结尾

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

重学java 65.IO流 缓冲流

I am not afraid tomorrow for I have seen yesterday and love today —— 24.6.5 一、字节缓冲流 1.字节缓冲流的意义 之前所写的FileOutputstream、FileInputstream、FileReader、Filewriter这都叫做基本流,其中FileInputstream和FieOutputstream的读写方法都是本地方法(方…

python字符串的进阶

在上一篇文章的 密码破解器 中&#xff0c;我们回顾了循环专题的知识点。 while 循环和 for 循环是 Python 中的两大循环语句&#xff0c;它们都可以实现循环的功能&#xff0c;但在具体使用时略有差别。当循环次数不确定时&#xff0c;我们选用 while 循环&#xff1b;当循环…

C#操作MySQL从入门到精通(10)——对查询数据进行通配符过滤

前言 我们有时候需要查询数据,并且这个数据包含某个字符串,这时候我们再使用where就无法实现了,所以mysql中提供了一种模糊查询机制,通过Like关键字来实现,下面进行详细介绍: 本次查询的表中数据如下: 1、使用(%)通配符 %通配符的作用是,表示任意字符出现任意次数…

DP读书:《ModelArts人工智能应用开发指南》(一)人工智能技术、应用平台

怎么用ModelArts人工智能应用 训练底座训练案例 盘古矿山模型Main config.py 训练底座 训练案例 盘古矿山模型 Main 下面是快速助手 https://support.huaweicloud.com/qs-modelarts/modelarts_06_0006.html 准备开发环境 在ModelArts控制台的“ 开发环境 > Notebook”页面…

MQTT协议使用总结

MQTT是基于TCP/IP协议栈构建的异步通信消息协议&#xff0c;是一种轻量级的发布/订阅信息传输协议MQTT在时间和空间上&#xff0c;将消息发送者与接受者分离&#xff0c;可以在不可靠的网络环境中进行扩展。适用于设备硬件存储空间有限或网络带宽有限的场景。 物联网平台支持设…

HarmonyOS应用开发深度指南:从基础到高级实践

1. HarmonyOS开发概述 HarmonyOS是华为推出的分布式操作系统,旨在为不同设备提供统一的体验。它支持多种编程语言,包括ArkTS、JS、C/C++和Java。开发者需要了解HarmonyOS的分布式架构,包括Ability、Service、Data Ability等核心概念。 了解HarmonyOS的分布式架构:HarmonyO…

每天CTF小练一点--ctfshow年CTF

初一 题目&#xff1a; 2023是兔年&#xff0c;密码也是。聪明的小伙伴们&#xff0c;你能破解出下面的密码吗&#xff1f; 感谢大菜鸡师傅出题 flag格式是ctfshow{xxxxxx}.或许密码也有密码。 密文是&#xff1a; U2FsdGVkX1M7duRffUvQgJlESPfOTV2i4TJpc9YybgZ9ONmPk/RJje …

【计算机组成原理】1.1计算机的软硬件组成(记录学习计算机组成原理)

文章目录 1.早期的冯诺依曼机2.早期冯诺依曼机的基本运行框图3.早期冯诺依曼机的特点4.现代计算机的结构5. 小结 本次及以后有关于计算机组成原理的文章&#xff0c;旨在做学习时的记录和知识的分享。不论是应对期末考试&#xff0c;还是考研都是很有帮助的。希望大家多多支持更…

Hotcoin精彩亮相Consensus 2024 Austin,探索行业风向标

5 月 31 日&#xff0c;由CoinDesk主办的“Consensus 2024”大会在德克萨斯州的奥斯汀市正式落下帷幕。作为全球规模最大、最具影响力的加密货币、区块链、Web3盛会&#xff0c;本次Consensus 2024 Austin吸引来自 100 多个国家/地区的 15,000 多名与会者、6,800 家公司、850 多…

动态代理(黑马笔记)

一、BigStar 大明星类 package com.itheima.mydynamicproxy1; public class BigStar implements Star {//实现接口要重写里边的抽象方法private String name;public BigStar() {}public BigStar(String name) {this.name name;}//唱歌Override //表示重写接口中的方法public…

【HarmonyOS】鸿蒙应用实现音效播放

一、问题背景&#xff1a; 应用在强提醒场景下&#xff0c;一般会有播放音效的效果&#xff0c;提示用户注意力的关注。 比如消息提醒&#xff0c;扫码提示&#xff0c;删除键确认提示等。 在鸿蒙应用如何实现音效播放呢&#xff1f; 二、解决方案&#xff1a; 使用AVPlaye…

园区运营管理平台的功能架构

产业园区作为推动地方经济发展的重要载体&#xff0c;其运营管理水平直接影响到园区的竞争力和可持续发展能力&#xff0c;园区运营管理平台作为园区的运营管理工具&#xff0c;旨在通过智能化、自动化的手段提升园区的运营效率和服务水平。 园区运营管理平台不仅为园区管理者提…

45-3 护网溯源 - 为什么要做溯源工作

官网:CVERC-国家计算机病毒应急处理中心 西工大遭网络攻击再曝细节!13名攻击者身份查明→ (baidu.com) 护网溯源是指通过技术手段追踪网络攻击的来源和行为,其重要性体现在以下几个方面: 安全防御:了解攻击源头可以帮助组织加强网络安全防御,及时采取措施防止攻击的再次…

A股冲高回落,金属、地产板块领跌,新股N汇成真首日暴涨753%

行情概述 AH股有色金属、教育及地产板块领跌&#xff0c;军工航天及半导体板块逆势走强&#xff1b;锂电池、创新药概念股也走强。创业板新股N汇成真首日暴涨753%&#xff0c;触发二次临停。 周三A股冲高回落&#xff0c;上证指数收跌0.83%&#xff0c;深成指跌0.8%&#xff…

爬楼梯——动态规划第一步

本问题其实常规解法可以分成多个子问题&#xff0c;爬第 n 阶楼梯的方法数量&#xff0c;等于两个部分之和 爬上 n−1 阶楼梯的方法数量。因为再爬 1 阶就能到第 n 阶爬上 n−2 阶楼梯的方法数量&#xff0c;因为再爬 2 阶就能到第 n 阶 所以我们得到公式 dp[n] dp[n−1] d…

Windows下Qt5.14.2连接华为IoTDA平台

一、华为IoTDA简介 华为云物联网平台&#xff08;IoT 设备接入云服务&#xff09;提供海量设备的接入和管理能力&#xff0c;将物理设备联接到云&#xff0c;支撑设备数据采集上云和云端下发命令给设备进行远程控制&#xff0c;配合华为云其他产品&#xff0c;帮助您快速构筑物…

力扣200. 岛屿数量(BFS)

Problem: 200. 岛屿数量 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.定义方向数组&#xff1a;定义一个方向数组 DIRECTIONS&#xff0c;表示上、下、左、右四个方向的移动。 2.获取网格的行数和列数同时初始化一个计数器 numIslands 用于记录岛屿的数量。 …

【新书上市】图像画质算法与底层视觉技术

图书主页&#xff1a;https://book.douban.com/subject/36895899/ 购买链接&#xff1a;https://item.jd.com/10105601481762.html 内容介绍 本书主要介绍了图像画质相关的各类底层视觉任务及其相关算法&#xff0c;重点讲解了去噪、超分辨率、去雾、高动态范围、图像合成与图…

计算机网络 —— 数据链路层(无线局域网)

计算机网络 —— 数据链路层&#xff08;无线局域网&#xff09; 什么是无线局域网IEEE 802.11主要标准及其特点&#xff1a; 802.11的MAC帧样式 我们来看看无线局域网&#xff1a; 什么是无线局域网 无线局域网&#xff08;Wireless Local Area Network&#xff0c;简称WLAN…

LLM之RAG实战(三十九)| 高级RAG技术全面解析(附代码)

一、高级RAG概述 基本 RAG 的工作流程可分为三个步骤&#xff1a;索引、检索和生成。在索引阶段&#xff0c;文本被转换为嵌入&#xff0c;然后存储在向量数据库中以创建可搜索的索引。在检索步骤中&#xff0c;用户的查询也被转换为嵌入&#xff0c;此嵌入用于在向量数据库中搜…