当前位置: 首页 > news >正文

Spring Data JPA源码

导读:

什么是Spring Data JPA?
要解释这个问题,我们先将Spring Data JPA拆成两个部分,即Sping Data和JPA。
从这两个部分来解释。
Spring Data是什么?
摘自: https://spring.io/projects/spring-data

Spring Data’s mission is to provide a familiar and consistent, Spring-based programming model for data access while still retaining the special traits of the underlying data store.It makes it easy to use data access technologies, relational and non-relational databases, map-reduce frameworks, and cloud-based data services. This is an umbrella project which contains many subprojects that are specific to a given database. The projects are developed by working together with many of the companies and developers that are behind these exciting technologies.
翻译:
Spring Data的使命/任务就是提供一个通用的/熟悉的和一致的,基于Spring的数据访问编程模型,同时仍然保留了底层数据存储的特性.
它使使用数据访问技术、关系和非关系数据库、map-reduce框架和基于云的数据服务变得容易。这是一个总的项目,包含许多特定于给定数据库的子项目。这些项目是通过与这些令人兴奋的技术背后的许多公司和开发人员合作开发的。

JPA是什么?

JPA全称:Java Persistence API
是一个基于ORM(对象关系映射)的标准规范,既然是规范,那自然不是具体的实现.
Hibernate就是其中的一个实现

JPA与JDBC的关系

JPA是Java持久化的API, JDBC是Java数据库连接.
JPA是JDBC上层的抽象,提供一种ORM即对象关系映射的方式来操作数据库。
JPA是基于JDBC的,就是说JPA的各种实现包括Hibernate,JPA是基于JDBC的更高级的数据库访问技术。
JDBC是java语言中用于连接和操作数据库的API,提供了一套标准接口和方法。然后基于各个数据库的的驱动来与数据库进行交互。

在图中可以反映JPA与JDBC的关系

源码

以一个简单的方法作为切入口了解一下Spring Data Jpa的源码
这里是基于SpringBoot 2.3.12.RELEASE版本来研究源码的。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
所以在maven的pom.xml中引用jpa.
根据spring-boot-autoconfigure的jar包中会有spring-autoconfigure-metadata.properties文件,
这里面配置了匹配jpa自动配置的条件。如下
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.ConditionalOnBean=javax.sql.DataSource
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.ConditionalOnClass=org.springframework.data.jpa.repository.JpaRepository
通过上述配置可知:要有数据源,要有JpaRepository以及HibernateJpaAutoConfiguration等
因为JPA是基于Hibernate来的,所以需要先有Hibernate所以也就必须先进行
HibernateJpaAutoConfiguration自动配置

整体流程

整体分析源码的流程是,围绕三个问题展开的,问题如下:
1.为什么我自己定义的AccountRepository接口的类型为SimpleJpaRepository,这是从哪里来的?
2.代理SimpleJpaRepositiry是在什么时候创建的执行的?
3.为什么根据accountRepository获取对象获取到的是JpaRepositoryFactoryBean这个类型的对象?

整体流程如下:

在这里插入图片描述

具体源码分析如下

关于Spring Data Jpa的源码, 我们可以通过一个简单的例子来作为切入口,例子如下
// 写一个单元测试的类
@RunWith(SpringRunner.class)
@SpringBootTest
public class AccountRepositoryTest {@Autowiredprivate AccountRepository accountRepository;@Testpublic void test() {Optional<Account> accountOptional = accountRepository.findById(1L);if (accountOptional.isPresent()) {Account account = accountOptional.get();System.out.println(account);}}
}

1. 运行单元测试方法发现accountRepository是一个SimpleJapRepository类型的代理对象

在这里插入图片描述
至此我们需要搞明白两个事情:1、为什么是SimpleJpaRepository,这是从哪里来的。2、代理对象是什么时候创建的
带着这两个问题我们去看源码。
熟悉Spring生命周期的应该知道AccountRepository接口上的@Repository注解会Spring扫描处理,并实例化。定位到具体代码

2. 进入Spring生命周期代码中查看对象的创建过程

// AbstractApplicationContext类的refresh() --> finishBeanFactoryInitialization(beanFactory)
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();

在这里插入图片描述
可以看到在这一步accountRepository已经是一个FactoryBean,所以if的这个分支会进入,在这里会对FactoryBean的对象调用getBean(String name)方法,从而进入Spring创建对象的生命周期中。
再看这个循环里面的beanNames来自于beanDefinitionNames即是存放beanDefinition对象集合的名称集合。进入isFactoryBean(String name)方法内容如下:
在这里插入图片描述
发现获取到的单例对象类型为JpaRepositoryFactoryBean,这里又增加了一个新的问题,即3、为什么根据accountRepository获取对象获取到的是JpaRepositoryFactoryBean这个类型的对象。
由于当代码执行到这一步的时候,说明BeanDifinition集合已经存在了,即Spring已经将类构造成BeanDifinition对象放入集合中,后续会根据BeanDifinition集合来创建对象。所以我们需要找到初始化BeanDifinition对象的地方。
根据beanDefinitionNames找到调用的添加操作的地方
在这里插入图片描述
找到了registerBeanDefinition方法,但是这个时候BeanDefinition已经是JapRepositoryFactoryBean了,还得往前找。
在这里插入图片描述

3.根据调用链找到了RepositoryConfigurationDelegate类的registerRepositoriesIn方法中调用了上面的registerBeanDefinition方法,该方法将AccountRepositoryBeanDefinition中注册为JpaRepositoryFactoryBean

在这里插入图片描述
先看builder.build(configuration)方法,在build方法中会获取到JapRepositoryFactoryBean类型并注册
在这里插入图片描述
build方法执行结束后,会使用返回的definitionBuilder获取beanDefinition对象,并将beanDefinition传入registerBeanDefinition方法
在这里插入图片描述
到这里就解释了,为什么accountRepository对应的BeanDefinition对象类型为JapRepositoryFactoryBean类型。在Spring中FactoryBean的作用就是生产某一种类型的对象,核心方法为T getObject() throws Exception;至此解释了一个问题:为什么根据accountRepository获取对象获取到的是JpaRepositoryFactoryBean这个类型的对象。

4. 查看JpaRepositoryFactoryBeangetObject()方法

接下来重点看下JpaRepositoryFactoryBeangetObject()方法
在这里插入图片描述
这里方法很简单,直接从this.respository中获取对象即可,但是this.repository是何时初始化的呢?查看JpaRepositoryFactoryBean的类图,发现实现了InitializingBean接口
在这里插入图片描述

5. getObject()方法中的this.repository的初始化

找到InitializingBean接口的实现方法,发现在afterPropertiesSet()方法中对this.respository进行初始化了。
在这里插入图片描述

6. SimpleJpaRepository的由来

进入getRepository(Class<T> repositoryInterface, RepositoryFragments fragments)方法进一步查看代码
在这里插入图片描述
再看这个方法getRepositoryInformation(metadata, composition) --> getRepositoryBaseClass(metadata)
在这里插入图片描述在这里插入图片描述
在初始化this.repository的时候,就固定将repository固定设置为SimpleJpaRepository类型了。
到这里就解释了为什么是SimpleJpaRepository类型了,回答了最开始提出的第一个问题。

7. AccountRepository代理对象的创建

此时回到RepositoryFactorySupport.getRepository(Class<T> repositoryInterface, RepositoryFragments fragments)方法
在这里插入图片描述
可以从中看到当设置了repositoryBaseClassSimpleJpaRepository类型之后,开始通过动态代理的方式来创建对象了。

Object target = getTargetRepository(information);中通过反射来创建SimpleJpaRepository对象,SimpleJpaRepository是实现了JpaRepository接口的,与我们自己定义的AccountRepository接口一样都实现了JpaRepository即可,这满足了JDK动态代理的条件,即需要代理的对象与代理对象都实现了相同的接口。

接下来使用ProxyFactory来创建代理对象的,从最开始的结果可以知道使用的是JDK的动态代理,所以需要传递需要代理的接口,
设置需要代理的接口,和实现类。通过最后的T repository = (T) result.getProxy(classLoader);来创建并获取代理对象。

	/*** Create a new proxy according to the settings in this factory.* <p>Can be called repeatedly. Effect will vary if we've added* or removed interfaces. Can add and remove interceptors.* <p>Uses the given class loader (if necessary for proxy creation).* @param classLoader the class loader to create the proxy with* (or {@code null} for the low-level proxy facility's default)* @return the proxy object*/public Object getProxy(@Nullable ClassLoader classLoader) {return createAopProxy().getProxy(classLoader);}

最终得到了accountRepository的代理对象,然后调用findById(ID id)

	@Overridepublic Optional<T> findById(ID id) {Assert.notNull(id, ID_MUST_NOT_BE_NULL);Class<T> domainType = getDomainClass();if (metadata == null) {return Optional.ofNullable(em.find(domainType, id));}LockModeType type = metadata.getLockModeType();Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();// 最终调用SessionImpl.find()方法查询返回结果return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));}

相关文章:

Spring Data JPA源码

导读: 什么是Spring Data JPA? 要解释这个问题,我们先将Spring Data JPA拆成两个部分&#xff0c;即Sping Data和JPA。 从这两个部分来解释。 Spring Data是什么? 摘自: https://spring.io/projects/spring-data Spring Data’s mission is to provide a familiar and cons…...

如何防止CSRF攻击

背景 随着互联网的高速发展&#xff0c;信息安全问题已经成为企业最为关注的焦点之一&#xff0c;而前端又是引发企业安全问题的高危据点。在移动互联网时代&#xff0c;前端人员除了传统的 XSS、CSRF 等安全问题之外&#xff0c;又时常遭遇网络劫持、非法调用 Hybrid API 等新…...

linuxARM裸机学习笔记(7)----RTC实时时钟实验

基础概念&#xff1a; I.MX6U 内部也有个RTC 模块&#xff0c;但是不叫作“ RTC ”&#xff0c;而是叫做“ SNVS ”。 SNVS 直译过来就是安全的非易性存储&#xff0c; SNVS 里面主要是一些低功耗的外设&#xff0c;包括一个 安全的实时计数器 (RTC) 、一个单调计数器 (mo…...

NSS [UUCTF 2022 新生赛]ez_upload

NSS [UUCTF 2022 新生赛]ez_upload 考点&#xff1a;Apache解析漏洞 开题就是标准的上传框 起手式就是传入一个php文件&#xff0c;非常正常的有过滤。 .txt、.user.ini、.txxx都被过滤了&#xff0c;应该是白名单或者黑名单加MIME过滤&#xff0c;只允许.jpg、.png。 猜测二…...

【操作系统】操作系统知识点总结(秋招篇)

文章目录 前言操作系统主要做了哪些工作&#xff1f;进程 线程 协程之间的区别进程的组成部分介绍一下进程的PCB讲一下进程的五态 以及它们的状态转移用户态和内核态是什么&#xff1f;进程在用户态和内核态之间是如何切换的讲一下进程之间的通信方式讲一下进程调度的三个层次介…...

篇十九:迭代器模式:遍历集合

篇十九&#xff1a;"迭代器模式&#xff1a;遍历集合" 开始本篇文章之前先推荐一个好用的学习工具&#xff0c;AIRIght&#xff0c;借助于AI助手工具&#xff0c;学习事半功倍。欢迎访问&#xff1a;http://airight.fun/。 另外有2本不错的关于设计模式的资料&…...

浅谈JVM中的即时编译器(Just-In-Time compiler, JIT)

Java虚拟机&#xff08;JVM&#xff09;中的即时编译器&#xff08;Just-In-Time compiler, JIT&#xff09;是一个非常重要的组件&#xff0c;它负责将字节码转换为本地机器代码。在不使用JIT的情况下&#xff0c;JVM通过解释字节码来执行程序&#xff0c;这意味着它会为每个字…...

Android 13 Launcher——长按图标弹窗内容修改以及小组件等隐藏起来

目录 一.背景 二.实现思路 三.布局文件修改 四.隐藏代码中原先的view 一.背景 由于定制化开发需要将原先的长按图标原生弹窗界面隐藏,然后显示自定义的弹窗界面,如下就是我们来实现自定义的弹窗界面...

又一个不可错过的编程大模型来了让你惊呼“码农人生”不虚此行

继Stable Diffusion爆火之后&#xff0c;StabilityAI近期又放大招&#xff0c;推出了号称是革命性的编程大模型StableCode。StableCode是其首款用于编码的LLM生成式AI产品&#xff0c;该产品旨在帮助程序员完成日常工作。目前已发布的版本为StableCode-Completion-Alpha-3B&…...

【Express.js】集成SocketIO

集成SocketIO 本节我们介绍在如何在 express 中集成 Socket.IO Socket.IO 算是 WebSocket 的一个超集&#xff0c;进行了一些封装和拓展。 准备工作 创建一个 express.js 项目&#xff08;本文基于evp-express-cli&#xff09;安装socket.io.js: npm i socket.io创建代理 …...

为树莓派Pico配置交叉编译环境和工具链arm-none-eabi-gcc时可能会遇到的错误以及解决方案

本文是一个类似手册的文章&#xff0c;用来记录可能遇到的错误。你可以通过侧栏选择遇到的错误来查看详细信息。 No install step for ‘ELF2UF2Build’ 遇到这种错误有两种原因&#xff1a; 安装了版本不对或者不完整的arm-none-eabi-gcc&#xff1b;没有使用正确的 C/C 的…...

Yum 部署K8S集群

目录 1、准备环境 &#xff08;温馨提示&#xff1a;尽量一次完成集群&#xff09; 2.安装master节点 3、安装k8s-master上的node 4、安装配置k8s-node1节点 5、安装k8s-node2节点 6、为所有node节点配置flannel网络 7、配置docker开启加载防火墙规则允许转发数据 一. 环…...

初阶C语言-操作符详解(下)

&#x1f31e; “等春风得意&#xff0c;等时间嘉许&#xff01;” 接下来&#xff0c;我们把操作符没学完的继续学完&#xff01; 操作符详解 6.2sizeof和数组 7.关系操作符8.逻辑操作符9.条件操作符10.逗号表达式11.下标引用、函数调用和结构成员12.表达式求值12.1隐式类型转…...

reposync命令——下载yum仓库中全部的包到本地

reposync命令可以将远端yum仓库里面的包全部都下载到本地。这样构建自己的yum仓库&#xff0c;就不会遇到网络经常更新包而头疼的事情了。 reposync命令在软件包 yum-utils 里面&#xff0c;需要保证yum-utils已安装。 yum install yum-utils -y 常用参数 -r &#xff1a;指定…...

LC-杨辉三角

LC-杨辉三角 链接&#xff1a;https://leetcode.cn/problems/pascals-triangle/submissions/ 上图就是一个杨辉三角&#xff0c;每个数等于他左上角的数与右上角的数之和。 第一行就是一个1&#xff1b;第二行是两个1&#xff1b;第三行的2就是它肩膀上两个1之和,其余的类似。…...

Golang空结构体struct{}的作用是什么?

文章目录 占位符&#xff1a;通道标识&#xff1a;键集合&#xff1a;内存占用优化&#xff1a;总结&#xff1a; 在Go语言中&#xff0c;空结构体 struct{}是一种特殊的数据类型&#xff0c;它不占用任何内存空间。空结构体没有任何字段&#xff0c;也没有任何方法。尽管它看起…...

自然语言处理从入门到应用——LangChain:提示(Prompts)-[示例选择器(Example Selectors)]

分类目录&#xff1a;《自然语言处理从入门到应用》总目录 如果我们拥有大量的示例&#xff0c;我们可能需要选择在提示中包含哪些示例。ExampleSelector是负责执行此操作的类。 其基本接口定义如下所示&#xff1a; class BaseExampleSelector(ABC):"""Interf…...

【实战项目】c++实现基于reactor的高并发服务器

基于Reactor的高并发服务器&#xff0c;分为反应堆模型&#xff0c;多线程&#xff0c;I/O模型&#xff0c;服务器&#xff0c;Http请求和响应五部分 ​全局 反应堆模型 Channel 描述了文件描述符以及读写事件&#xff0c;以及对应的读写销毁回调函数&#xff0c;对应存储ar…...

Docker部署ElasticSearch7

前言 帮助小伙伴快速部署研发或测试环境进行学习测试。springboot版本需要与ElasticSearch版本想对应&#xff0c;不同版本api不一致&#xff0c;会产生异常调用的情况。 一、拉取镜像 这里选择固定版本7.15.2 docker pull docker.elastic.co/elasticsearch/elasticsearch:…...

【算法|数组】滑动窗口

算法|数组——滑动窗口 引入 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度**。**如果不存在符合条件的子数组&#xff0c;返回 0 。 示例…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...