新鲜速递:Spring Data JPA 3.0快速入门、进阶到精通

news2024/9/22 4:09:39

第一章、安装Spring Data JPA

第一步,先确保你使用的是Spring Boot 3.0或以上环境,可以在pom.xml里加入Spring Data JPA依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

第二步,配置你的Spring Data JPA,这里一定要有一个可以连接的数据库,这里以MySQL为例

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: "jdbc:mysql://127.0.0.1:3306/数据库名?useUnicode=true&characterEncoding=utf8&reWriteBatchedInserts=true&autoReconnect=true&maxReconnects=2&initialTimeout=5"
    username: 用户名
    password: 密码
  jpa:
    database: MySQL
    show-sql: true
    generate-ddl: true
    hibernate:
      ddl-auto: none

至此安装完成

第二章、常规用法

首先我们要知道Spring Data JPA的作用,Spring Data JPA是一种基于JPA技术的ORM框架封装,相当于把EclipseLink或Hibernate加了一层壳,所以本质上你调用的还是Hibernate。
根据JPA的规范,我们要进行读写数据库,靠的是对实体(Entity)的定义,每一个实体都对应了一张表,这一点上并不是JPA的独创,大多数ORM框架都是这么做的,这么想就很容易理解了。
在进行实体操作的时候,传统方式是通过DAO(数据访问对象)进行操作,但到了JPA,你其实不必定义DAO,使用实体管理器即可进行实体操作和查询,用于取代DAO的则是Repository(仓库)。

第一步,我们定义一个实体,为了省略Getter和Setter方法,这里使用了Lombok的@Data注解:

import jakarta.persistence.*;
import lombok.Data;

@Entity
@Table(name = "student")
@Data
public class Student {
    @Id
    @Column(name = "uuid")
    @GeneratedValue(strategy = GenerationType.UUID)
    private String uuid;

    @Column(name = "name")
    private String name;

    @Column(name = "age")
    private Integer age;
}

这里需要注意的是,对于日期类的字段,请加上@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")注解,否则很多情况下你的时间格式是不统一的,这里的GMT+8指的就是东八区时区,如果你做外贸系统,需要根据实际情况调整。

第二步,写Repository,泛型第一个是实体类,第二个是主键类型,这里主键用的是UUID,所以是String

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends CrudRepository<Student,String> {
    Student getStudentByUuid(String uuid);
}

这里不用给出任何具体实现,因为实现都是由Spring Data JPA自动为你写好的,只需要按规则把方法名写好即可,一般都是用find、get、delete开头,IDE会自动帮你提示出方法名。
在这里插入图片描述
这里你可以让接口继承RepositoryCrudRepositoryJpaRepository

无论是你想控制边界避免程序员随意调用未经许可的接口方法,还是想手动优化,让所有的方法都要自己定义,请使用Repository。
如果你想做一些简单的增删改查,请使用CrudRepository。
如果想要分页、多条件查询等常规功能,请使用JpaRepository。
其中JpaRepository是功能最为强大的,但也同时是可优化程度最低的,适用于小型项目。

第三步,注入到Controller或者Service层里,调用即可

import jakarta.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/student")
@Transactional(rollbackFor = Exception.class)
public class StudentController {
    @Resource
    StudentRepository studentRepository;

    @GetMapping("/get")
    public Student get(String uuid){
        return studentRepository.getStudentByUuid(uuid);
    }
}

至于常规的增删改查直接就自带了,直接用即可
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第三章、进阶用法

如果你实际做项目,并不是只需要这点增删改查就可以的,做做毕业设计也就罢了。我们需要的是对SQL语句的整体把控,能够进行手动优化,甚至封装框架。

在Repository中自定义JPQL或SQL查询

是不是做项目的时候发现Repository方法名写的太长了,可读性非常差呢?这里我们可以在方法上加上@Query注解,例如:

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MaterialRepository extends CrudRepository<Material,String> {
    @Query(value = "select * from material where enabled = ?1",nativeQuery = true)
    List<Material> queryValidMaterial(Boolean enabled);
}

这里的nativeQuery如果是true,用的是SQL查询,如果是false,用的就是JPQL查询。而enabled就是咱们自己定义的参数,Material是实际业务系统中物料表的表名。
而value中所写的?1代表第一个参数,以此列推?2就代表第二个参数,如果你觉得这么写在长语句中不太直观,那也可以将占位符改为:enabled这样的形式,比如:

    @Query(value = "select * from material where enabled = :enabled",nativeQuery = true)
    List<Material> queryValidMaterial(@Param("enabled") Boolean enabled);

使用EntityManager直接进行查询

EntityManager是JPA中的实体管理器,它的实例是与线程相关的,所以可以很方便的在Spring管理的任何地方进行查询。简单的来说,我们可以用EntityManager来替代Repository,这对于要自己封装框架的读者们会非常友好。
下面这个例子就是使用EntityManager进行SQL查询并返回结果的例子:

@RestController
@RequestMapping("/api/material")
@Transactional(rollbackFor = Exception.class)
public class MaterialController {
    @PersistenceContext
    EntityManager entityManager;

    @GetMapping("/get")
    public Material get(String uuid){
    	// 创建SQL查询,第一个参数是SQL,第二个参数是要自动ORM的实体
        Query query = entityManager
                .createNativeQuery("""
                    SELECT * FROM material
                    WHERE uuid = :uuid
                """,Material.class);
        // 设置预编译参数,如果是?1、?2或者直接?做占位符的,第一个参数应当为从1开始的数字
        query.setParameter("uuid",uuid);
        // 获取一个结果并返回,如果需要多个结果,则调用getResultList()
        Material result = (Material)query.getSingleResult();
        return result;
    }
}

这里如果直接使用createQuery方法,则表明使用JPQL。因为很多人是MyBatis Plus转的,所以这里就用NativeQuery的写法,比较容易被读者接受。EntityManager可以说是JPA的核心,很多有趣的用法大家可以自行探索一下。

结合Spring Security自动写入审计信息

我们经常需要让系统自动维护创建人、创建时间、最后修改人、最后修改时间等信息,如果反复写这些代码会比较麻烦,最好的做法是让框架直接替咱们写进去。
第一步,创建自动审计信息写入的配置

import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.Optional;

@Configuration
public class JpaAutoAuditor implements AuditorAware<String> {

    @Override
    public Optional<String> getCurrentAuditor() {
        SecurityContext context = SecurityContextHolder.getContext();
        if (context == null) return Optional.of("");
        Authentication authentication = context.getAuthentication();
        if (authentication == null) return Optional.of("");
        if (!authentication.isAuthenticated()) return Optional.of("");
        return Optional.of(authentication.getName());
    }
}

第二步,我们同步在实体类(Entity)里创建对应的审计字段

    /**
     * 创建人
     */
    @CreatedBy
    @Column(name = "create_by")
    private String createBy

    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @CreatedDate
    @Column(name = "create_time")
    private LocalDateTime createTime

    /**
     * 最后修改人
     */
    @LastModifiedBy
    @Column(name = "last_modified_by")
    private String lastModifiedBy

    /**
     * 最后修改时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @LastModifiedDate
    @Column(name = "last_modified_time")
    private LocalDateTime lastModifiedTime

这样便可以在保存和更新的时候自动填充时间和创建人、修改人了。注意JsonFormat引用的是com.fasterxml.jackson.annotation.JsonFormat

分页

其实分页这块虽然叫做进阶用法,但是大多数情况还是根据实际业务来判断如何分页的。
如果你用Repository的方式,可以按如下方式进行分页

	@Query(value = "SELECT * FROM material WHERE type = ?1",
	 countQuery = "SELECT count(1) FROM material WHERE type = ?1",
	 nativeQuery = true)
	Page<Material> findByType(Integer type, Pageable pageable);

这里的countQuery是用来确定总数量的SQL语句,调用的时候pageable这么传:PageRequest.of(1,20),就代表取第一页的数据,每页20条

当然,如果因为查询效率不想用这个特性(比如跨库查询时往往会自定义一个KV库做查询堆),也可以手动写limit或rowno查询子句。

事务控制

上面的例子里大家会看到@Transactional注解,这个注解所标注的类或方法就会开启事务特性。如果你使用org.springframework.transaction.annotation.Transactional而不是jakarta的Transactional注解,还可以定制rollBackFor属性,当发生特定异常时才会进行回滚操作。

但有时候我们可能会做个EventBus封装一些消息,当执行过程到达某些错误消息的时候应当使之前所做的处理都恢复到原状,或者我们需要更加精细化的事务控制,这时候就会用到手动回滚。

// 无论在哪里执行,回滚当前线程下的事务(但不会立即回滚)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// 用EntityManager开启事务
entityManager.getTransaction().begin();
// 用EntityManager提交事务
entityManager.getTransaction().commit();
// 用EntityManager回滚事务
entityManager.getTransaction().rollback();
// 用EntityManager标记为应当回滚
entityManager.getTransaction().setRollbackOnly();

这里会涉及到事务传播机制,想必大家之前应该都学习过这个内容。

其他用法

有兴趣的读者可以搜索以下进阶用法,难度都不是很大,展开的去讲会比较跑题,所以这里不做赘述,短时间内突破即可快速全面掌握Spring Data JPA的用法:

1. Spring Data JPA 配置 Druid数据源
2. Spring Data JPA 的查询缓存配置
3. Hibernate的三态变化
4. Spring Data JPA + JTA(Atomikos)做分布式事务
5. Spring Data JPA 多数据源

有兴趣的读者可以自行研究、写博客并告诉我,我会在这里附上链接。

第四章、精通领域

前面的知识已经足够可以覆盖你的日常业务,如非必要,这一章可以略过,研究这一章的内容会消耗大量时间和精力。

Spring Data JPA内部机制

Jpa的抽象适配器位于AbstractJpaVendorAdapter,其实现有两个,一个是EclipseLinkJpaVendorAdapter,另一个是HibernateJpaVendorAdapter
在启动时会通过getJpaPropertyMap方法构筑配置项。

由此EntityManagerFactory和EntityManager会被构建起来。在这个过程中,SpringHibernateJpaPersistenceProvider会提供其实现。实现将被定位到Hibernate的SessionFactoryImpl和SessionImpl。

所以当我们每次调用EntityManager实际上调用的是Hibernate的Session。

除此之外,Repository的实现是SimpleJpaRepository,你的各种自定义方法实现都是基于JdkDynamicAopProxy的动态代理。

Criteria查询

你可以利用Criteria查询可以将大多数的查询机制进行二次封装,而且借助JPQL的特性,与具体数据库方言无关。如果你希望做一款能支持多种数据库系统的BI,Criteria可能会是一个不错的选择。
例如刚才查询学生UUID的过程就可以这样写:

        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Student> query = builder.createQuery(Student.class);

        Root<Student> root = query.from(Student.class);

        query.where(builder.and(
                builder.equal(root.get("uuid"), uuid)
        ));

        return entityManager.createQuery(query).getSingleResult();

使用该工具可以很方便的实现数据湖、BI等产品

GraphQL

当你使用Spring Data JPA之后就可以无缝使用GraphQL了。
第一步,引入spring-boot-starter-graphql
第二步,开启GraphQL

spring
	graphql
		graphiql
			enabled:true

第三步,Spring Boot会在src/main/resources/graphql/**目录寻找结尾为.graphqls.gqls的文件,这些文件里可以这样描述我们的GraphQL查询结构:

type Student {
    uuid: ID!
    name: String!
    age: Int
}
type Query {
    allStudents: [Student]!
    studentByUuid(uuid: ID): Student
}
type Mutation {
    createStudent(studentInput: StudentInput): Student
}
input StudentInput {
    uuid: ID!
    name: String!
    age: Int
}

第四步,在Controller的方法上标注@QueryMapping注解、在参数上标注@Argument就可以执行查询了。如果是增删改操作,则加上@MutationMapping注解即可。

第五章、总结

为了能最大限度的让读者开箱即用Spring Data JPA,这里有很多细节问题没有展开的去说,大多数的专业术语已经在文中给出提示。
总的来说,Spring Data JPA是一个非常不错的持久化层框架,以渐进式的方式可以帮助我们快速搭建应用到深度使用。随着多行字符串语法糖的普及,很多SQL语句都可以直接写在Java文件中,相较于MyBatis写在XML的做法而言,一来更容易保护我们的知识产权,二来更可以避免因为服务器被黑客提权从而修改XML进行二次攻击的风险,第三还可以以一种与数据库方言无关的方式完成跨数据库平台的应用系统设计。

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

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

相关文章

【ROS】—— ROS通信机制——话题通信(二)

文章目录前言1. 话题通信1.1 话题通讯理论模型1.2 话题通信基本操作&#xff08;C&#xff09;1.2.1 简单发布框架的实现1.2.2 发布逻辑的实现1.2.3 订阅方的实现1.3 话题通信基本操作&#xff08;python&#xff09;1.3.1 发布的实现1.3.2 订阅的实现1.4 话题通信自定义msg1.4…

五问补盲(五)| 想要长得好看又好用,补盲激光雷达应该怎么做?

上期&#xff0c;我们聊了好用的补盲激光雷达&#xff0c;得满足哪些条件&#xff1f; 好用是必备素质&#xff0c;属于补盲激光雷达的底线。好用之外&#xff0c;补盲激光雷达还有一种更直观的竞争力&#xff0c;那就是——外型。 有句话说的好&#xff0c;很多时候&#xff0…

2022跟学尚硅谷Maven入门(二)IDEA操作

2022跟学尚硅谷Maven入门二 IDEA操作第四章 使用Maven&#xff1a;IDEA环境第一节 创建父工程1.创建 Project2.开启自动导入第二节 配置 Maven 信息第三节 创建 Java 模块工程第四节 创建Web模块工程1.创建模块2.修改打包方式3.Web 设定4.借助IDEA生成web.xml5.设置 Web 资源的…

数据库,计算机网络、操作系统刷题笔记21

数据库&#xff0c;计算机网络、操作系统刷题笔记21 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle…

基于thinkphp6搭建的 admin后台管理基础框架,方便快速进行二次开发

小牛Admin v2.1 完整代码下载地址&#xff1a;基于thinkphp6搭建的 admin后台管理基础框架 使用thinkphp6 layui 搭建的 admin后台管理基础框架&#xff0c;方便快速进行二次开发 该项目是在 http://www.xnadmin.cn/ 小牛Admin 开源项目的基础上进行个人优化的产物 运行环境…

Redis-用户签到UV统计

一、用户签到 1.1 BitMap用法 我们按月来统计用户签到信息&#xff0c;签到记录为1,未签到记录为0 把每一个bit位对应当月的每一天&#xff0c;形成了映射关系。用0和1表示业务状态&#xff0c;这种思路就称为位。Redis中是用利用string类型数据结构实现BitMap&#xff0c;因…

使用pip命令时,报错:_sysconfigdata_x86_64_conda_cos7_linux_gnu.py

问题&#xff1a; 在linux服务器中想使用pip命令pip show list查看安装了哪些包时&#xff0c;报错 ModuleNotFoundError: No module named ‘_sysconfigdata_x86_64_conda_cos7_linux_gnu’ 问题原因 原因是&#xff1a;在当前的环境下的python中丢失了一个备份文件&#xf…

经典算法之常用排序

目录❤️前言&#x1f498;一、分治思想&#x1f49e;二、归并排序1.实现方法2.动图分析3.代码模板&#x1f496;三、快速排序1.实现方法2.动图分析3.代码模板❤️前言 本文介绍两种基于分治思想的经典排序算法&#xff1a; 归并排序与快速排序 &#x1f498;一、分治思想 分…

【Python从入门到进阶】1、初识Python

一、Python的起源 1989年&#xff0c;为了打发圣诞节假期&#xff0c;荷兰程序员Gudio van Rossum吉多●范罗苏姆(龟叔&#xff0c;下图穿“人生苦短&#xff0c;我用Python”T恤衫的老哥)决心开发一个新的解释程序&#xff08;Python维形&#xff09;; 1991年&#xff0c;第一…

机器学习中的数学原理——向量内积

今天是2023年的第一天&#xff0c;祝大家新年快乐&#xff01;这个专栏主要是用来分享一下我在机器学习中的学习笔记及一些感悟&#xff0c;也希望对你的学习有帮助哦&#xff01;感兴趣的小伙伴欢迎私信或者评论区留言&#xff01;这一篇就更新一下《白话机器学习中的数学——…

【C语言】指针

文章目录指针作为参数的指针访问某个地址上的变量*指针的应用数组和指针数组变量是特殊的指针指针常量和常量指针数组指针和指针数组常见错误指针运算动态内存分配指针 一般用p来表示一个指针&#xff0c;来自pointer 一个指针类型的变量就是保存地址的变量。 变量的值是内存…

windows x32调用门实现 ring3提权

概述 调用门是Intel提供的一个机制&#xff0c;用于控制不同权限级(ring0-ring3)的程序函数调用。简单点就是提供了一个ring3 调用ring0 函数的机制。 在intel手册volume3-Chapter 5.83描述如下 Call gates facilitate controlled transfers of program control between dif…

算法笔记(25)win10系统安装tensorflow-GPU环境亲测好用

环境准备 首先你的电脑得有GPU显卡&#xff0c;然后在tensorflow官网&#xff08;在 Windows 环境中从源代码构建 | TensorFlow&#xff09;找到各安装软件对应的版本&#xff0c;版本不对应将会导致安装失败。 标本机GPU显卡版本题本文选择的是官网最新的组合&#xff1a;p…

cubeIDE开发, stm32窗口看门狗WWDG的CubeMX配置及HAL库底层实现分析

一、stm32的WWDG说明 1.1 WWDG特点&#xff1a; 在前一篇博文介绍独立看门狗时就指出STM32 MCU提供两个看门狗&#xff0c;独立看门狗和窗口看门狗。 cubeIDE开发&#xff0c; stm32独立看门狗IWDT的CubeMX配置及HAL库底层实现分析_py_free的博客-CSDN博客 相比独立看门狗&am…

【C语言】数据类型

文章目录c语言的类型整数整数类型整数的内部表达整数的范围unsigned整数的格式化浮点型浮点类型输入输出输出精度字符类型逃逸字符类型转换自动类型转换强制类型转换逻辑类型bool逻辑运算条件运算符C语言的变量&#xff0c;必须在使用前定义&#xff0c;并且确定类型&#xff1…

ubuntu下使用doxygen生成软件文档

ubuntu下使用Doxygen生成软件文档一、软件下载1.系统安装Doxygen2.Vscode插件安装二、软件配置三、文档生成1.方法1&#xff1a;使用Doxygen-gui生成&#xff08;推荐&#xff09;2.方法&#xff1a;采用Doxygen命令&#xff08;不推荐&#xff09;另外Windows下使用方法提示&a…

【JavaScript】 Date 日期对象概述及相关方法

文章目录【JavaScript】 Date 日期对象的创建及相关方法一. 日期对象的创建二. 日期对象的相关方法三. 时间戳案例案例1&#xff1a;在页面上展示一个时钟&#xff0c;隔1s更新一次案例2&#xff1a;距离除夕倒计时【JavaScript】 Date 日期对象的创建及相关方法 一. 日期对象…

ArcGIS基础:构建点对连线表达点集内部相互关系

原始数据如下&#xff0c;为普通的点图层&#xff0c;总共是21个点。 点位分布如下&#xff1a; 属性表打开如下&#xff1a; 下面使用【构造视线】工具进行操作&#xff0c;其工具位于【3D分析工具】下的【可见性】工具栏。 打开【构造视线】对话框&#xff0c;把【视点…

Leetcode【494. 目标和】

题目描述 给你一个整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 ‘’ 或 ‘-’ &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 例如&#xff0c;nums [2, 1] &#xff0c;可以在 2 之前添加 ‘’ &#xff0c;在 1 之前添加 ‘-’ &…

第10章 角色页的分页、排序、查询实现

1 重构WebApi.Controllers.RoleController. PostRolePageByFromBodyAsync //把所有符合条件的角色实例&#xff0c;按照指定字段进行排序操作。 if (!string.IsNullOrEmpty(pagination.OrderByFiled)) { var _obj JsonConvert.DeserializeAnonymousType(pagination.OrderByFil…