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

Spring boot 知识整理

本文章主要是将SpringBoot开发过程的CRUD进行介绍,以及对于初学者来讲,对应的注解内容进行解释。
.
项目的源代码:https://github.com/Gaonavak/SpringBoot
.
欢迎来访的批评指正🌹🌹,如果可以的话给一下关注和点赞,谢谢支持🫡🫡

一、SpringBoot 背景内容梳理

  • SpringBoot是一个基于Spring框架的开源框架,用于简化Spring应用程序的初始搭建和开发过程。它通过提供约定优于配置的方式,尽可能减少开发者的工作量,使得开发Spring应用变得更加快速、便捷和高效。

  • SpringBoot的主要特点包括:

  1. 简化配置:SpringBoot遵循约定优于配置的原则,减少了传统Spring应用中的大量配置。它通过自动配置(auto-configuration)和起步依赖(starterdependencies)来简化项目的配置过程,让开发者可以快速搭建起一个可运行的Spring应用。
  2. 集成性强:SpringBoot提供了大量的开箱即用的特性和功能,如内嵌的Servlet容器(如Tomcat、Jetty或Undertow)、健康检查、指标监控等。它还整合了诸多常用的库和框架,如SpringData、SpringSecurity等,使得开发者可以快速构建出功能完善的应用。
  3. 微服务支持:SpringBoot非常适合用于构建微服务架构。它提供了丰富的支持,如通过SpringCloud进行微服务架构的开发,集成了服务发现、配置中心、负载均衡等功能,帮助开发者构建可伸缩、高可用的微服务系统。
  4. 内嵌服务器:SpringBoot可以将应用程序打包成一个可执行的JAR文件,并内置了常用的Servlet容器,如Tomcat、Jetty或Undertow。这样一来,开发者可以通过简单的java-jar命令来运行应用程序,而无需部署到外部应用服务器。
  5. 生态丰富:由于SpringBoot的广泛应用和强大生态系统,开发者可以轻松地使用各种扩展和插件,如Actuator、SpringBootDevTools等,提高开发效率和应用质量。
  • 从前端的api到我后端的接受再到数据库:
    • 发请求——>控制层接受——> 调用服务层的函数——>调用Repository——>找到对应的实体然后进行数据库的交互

其中额外设计的有 Response 的设计 ,方便前端对于后端返回数据的统一规范处理;
DTO类(用于封装数据,通常在层与层之间传递,避免直接暴露实体);
转换器的设计,方便后端设置哪些数据可以返回给对应的前端

二、Rest api 规范

  • 路径

    • 路径又称"终点”(endpoint),表示API的具体网址。
    • 在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。
  • Http 动词

    • GET(SELECT):从服务器取出资源(一项或多项)。
    • POST(CREATE):在服务器新建一个资源。
    • PUT(UPDATE):在服务器更新资源(将户端提供改变后的完整资源)。
    • PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
    • DELETE(DELETE):从服务器删除资源。

三、JPA

JPA 提供了一种标准化的方式来进行对象关系映射,是 Java 应用程序中进行数据持久化的关键技术。通过使用 JPA,开发者可以更高效地管理数据库操作,专注于业务逻辑开发

JPA 是一个规范,多个框架实现了该规范,常见的实现包括:

  • Hibernate:最流行的 JPA 实现,提供丰富的功能和强大的社区支持。
  • EclipseLink:Oracle 官方的 JPA 实现,支持多种数据库。
  • OpenJPA:Apache 提供的 JPA 实现。

3.1. JPA内部有关的注解

1. @Entity

  • 定义:标识一个类为 JPA 实体,表示该类对应于数据库中的一张表。

2. @Table

  • 定义:指定实体类对应的数据库表的名称。
  • 用法@Table(name = "table_name")

3. @Id`

  • 定义:标识实体类的主键字段。

4. @GeneratedValue`

  • 定义:指定主键的生成策略。
  • 常用策略
    • GenerationType.IDENTITY:主键由数据库自动生成。
    • GenerationType.SEQUENCE:使用数据库序列生成主键。
    • GenerationType.AUTO:根据数据库自动选择生成策略。

5. @Column`

  • 定义:映射实体类的字段到数据库表的列。
  • 常用属性
    • name:指定数据库列名。
    • nullable:指定该列是否可以为 null
    • length:指定字符串列的最大长度。

6.@Builder

  • 用于简化对象的构建过程。它通过生成一个构建器模式的实现,减少频繁的属性赋值
  • 用法:
Student student = Student.builder() .name("Alice") .age(20).email("alice@example.com") .build();

3.2. JPA 的使用(JpaRepository<T, ID>)

  • JpaRepository<T, ID>是一个 Spring Data JPA 提供的接口,允许你执行 CRUD 操作。

    • 第一个泛型参数 T 表示实体类的类型,即与数据库相对应的Java类
    • 第二个泛型参数 ID 表示主键的类型,即用于唯一标识实体的字段,确保记录的唯一性(通常是 Long, Integer, String 等)
    • 必须遵循 JpaRepository<实体类, 主键类型> 的格式。交换位置会导致错误。
  • JPA的方法用法:

    • 基本 CRUD 操作
      • 通过继承 JpaRepository,可以获得基本的 CRUD 操作,如 save(), findById(), delete(), findAll() 等。
    • 查询方法命名约定
      • JPA 提供了一种约定优于配置的方法命名规则。你可以通过特定的命名规则自动生成查询。
      • 示例:
        • findByEmail(String email):根据 email 查找学生。
        • findByAgeBetween(int min, int max):查找年龄在 minmax 之间的学生。
        • findByNameStartingWith(String namePrefix):查找姓名以 namePrefix 开头的学生。
    • 使用 @Query 注解
      • 可以使用 @Query 注解来定义自定义查询。
      • 原始 SQL 查询
        • 使用 nativeQuery = true 指定这是一个原始 SQL 查询。
        • 示例:
@Query(value = "select * from Student   where  email = :email", nativeQuery = true)  
List<Student> findByEmail2(@Param("email") String email);query里面的用 :email 做占位符,用来读取@Parm 中的形参

.
- JPQL 查询
- JPQL 用于面向对象的查询,允许你直接操作实体。
- 使用 SELECT NEW 创建新对象。
- 示例:`findByEmail3(@Param(“email”) String email)

@Query(value = "SELECT NEW com.demo.springbootstudy.dao.Student(s.name, s.email) FROM Student s WHERE s.email = :email")  
List<Student> findByEmail3(@Param("email") String email);query里面的用 :email 做占位符,用来读取@Parm 中的形参

3.3 JpaSpecificationExecutor< T > 的使用

JpaSpecificationExecutor 是 Spring Data JPA 提供的接口,用于支持动态查询。通过该接口,可以使用 JPA Criteria API 构建复杂的查询条件.
.
在面对一个大项目,里面复杂的查询情况,可以单独创建一个文件夹存放不同实体的 Specification的静态定义,后续在服务层根据对应的情况,设置不同属性进行传参调用静态函数

使用:

  • 创建Repority接口去继承 JpaSpecificationExecutor< T >(其中T指的是数据库表实体)
  • 定义一个类存放Specification的静态方法,模板如下:
public static Specification<Student> filterByCriteria(StudentDTO studentDTO) {  return (root, query, cb) -> {  List<Predicate> predicates = new ArrayList<>();  if (studentDTO.getName() != null) {  predicates.add(cb.equal(root.get("name"), studentDTO.getName()));  }  if (studentDTO.getMinAge() != null) {  predicates.add(cb.greaterThanOrEqualTo(root.get("age"), studentDTO.getMinAge()));  }  if (studentDTO.getMaxAge() != null) {  predicates.add(cb.lessThanOrEqualTo(root.get("age"), studentDTO.getMaxAge()));  }  //两个 return 实现的效果一样return query.where(predicates.toArray(new Predicate[0])).getRestriction();return cb.and(predicates.toArray(new Predicate[0]));  };  
}- `root`:表示查询的根实体,即在这里是 `Student`。
- `query`:表示查询的上下文。
- `cb`:表示构建条件的 `CriteriaBuilder`对象。- 其中这个cb里面集成多种方法用于构建查询条件- cb.and(predicates.toArray(new Predicate[0])); - 动态地将所有的查询条件结合起来,形成一个整体的查询条件
- `Predicate`:表示查询条件,通过add函数来添加不同的查询条件- .getRestriction() 用于获取它所表示的条件
  • 后续调用先构建好对应的形参,然后调用上面的静态方法获取Specification;
  • 最后调用Repository接口中的函数findAll,形参传入的是上面的Specification对象;

3.4 分页查找的用法

四、Mybatis 的用法

MyBatis 是一个 持久层框架,用于简化数据库操作。它提供了一种映射关系,将数据库中的记录映射到 Java 对象,并支持通过 XML 或注解的方式配置 SQL 语句。
.
MyBatis Plus 是在 MyBatis 的基础上开发的增强工具包,旨在简化开发,提高效率。它提供了 CRUD 操作、条件构造器、分页插件、代码生成器等多种功能。

4.1 配置

Pom.xml 配置

<dependencies><!-- MyBatis Plus 依赖 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><!-- MySQL 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.23</version></dependency><!-- Spring Boot Starter Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency><!-- 其他依赖 --><!-- ... -->
</dependencies>

Spring Boot 配置(以 .yml 文件为主)

spring:datasource:url: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: your_usernamepassword: your_passworddriver-class-name: com.mysql.cj.jdbc.Drivermybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl //sql打印到控制台

编写实体类

@Data
@TableName(value = "user")
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;private String email;// @TableField(fill = FieldFill.INSERT, value = "create_time")// private Long createTime;// @TableField(fill = FieldFill.INSERT_UPDATE, value = "update_time")// private Long updateTime;// @Version// private Long version;
}

编写Mapper接口

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

添加注解MapperScan

在Application类上添加@MapperScan注解并指定mapper的包路径

Mybatis-plus 拦截器配置

配置类用于添加拦截器,如乐观锁、分页等插件

@Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加乐观锁拦截器 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());// 添加分页拦截器interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; }
}

4.2 常用功能介绍

1. CRUD 操作

MyBatis Plus 提供了基础的 CRUD 方法,包括 insertdeleteupdateselect 等。

    @Testvoid testInsert() {User user = User.builder().name("fengzhu").age(18).email("abc@a.com").build();userMapper.insert(user);}@Testvoid testSelect() {User user = userMapper.selectById(24L);System.out.println(user.getName());}@Testvoid testUpdate() {User user = User.builder().id(27L).email("fasfas@a.com").build();int count = userMapper.updateById(user);System.out.println("update success:" + count);}@Testvoid testDelete() {userMapper.deleteById(24L);}

2. 条件构造器

条件构造器主要涉及到3个类,AbstractWrapperQueryWrapperUpdateWrapper

在AbstractWrapper中提供了非常多的方法用于构建WHERE条件,而QueryWrapper针对SELECT语句,提供了select()方法,可自定义需要查询的列,而UpdateWrapper针对UPDATE语句,提供了set()方法,用于构造set语句,条件构造器也支持lambda表达式。

AbstractWrapper中用于构建SQL语句中的WHERE条件的方法进行部分列举

  • eq:equals,等于
  • allEq:all equals,全等于
  • ne:not equals,不等于
  • gt:greater than ,大于 >
  • ge:greater than or equals,大于等于≥
  • lt:less than,小于<
  • le:less than or equals,小于等于≤
  • between:相当于SQL中的BETWEEN
  • notBetween
  • like:模糊匹配。like(“name”,“黄”),相当于SQL的name like ‘%黄%’
  • likeRight:模糊匹配右半边。likeRight(“name”,“黄”),相当于SQL的name like ‘黄%’
  • likeLeft:模糊匹配左半边。likeLeft(“name”,“黄”),相当于SQL的name like ‘%黄’
  • notLike:notLike(“name”,“黄”),相当于SQL的name not like ‘%黄%’
  • isNull
  • isNotNull
  • in
  • and:SQL连接符AND
  • or:SQL连接符OR
  • apply:用于拼接SQL,该方法可用于数据库函数,并可以动态传参
// 条件查询
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "Tom");
List<User> users = userMapper.selectList(queryWrapper);// 条件更新
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "Tom").set("age", 27);
userMapper.update(updateWrapper);

3. Condition

条件构造器的诸多方法中,均可以指定一个boolean类型的参数condition,用来决定该条件是否加入最后生成的WHERE语句中,比如

String name = "黄"; // 假设name变量是一个外部传入的参数
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like(StringUtils.hasText(name), "name", name);
// 仅当 StringUtils.hasText(name) 为 true 时, 会拼接这个like语句到WHERE中
// 其实就是对下面代码的简化
if (StringUtils.hasText(name)) {wrapper.like("name", name);
}

4. 实体对象作为条件

调用构造函数创建一个Wrapper对象时,可以传入一个实体对象。后续使用这个Wrapper时,会以实体对象中的非空属性,构建WHERE条件(默认构建等值匹配的WHERE条件,这个行为可以通过实体类里各个字段上的@TableField注解中的condition属性进行改变)

@Test
public void test3() {User user = new User();user.setName("abc");user.setAge(28);QueryWrapper<User> wrapper = new QueryWrapper<>(user);List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);
}

5. allEq方法

  • allEq方法传入一个map,用来做等值匹配
  • 当allEq方法传入的Map中有value为null的元素时,默认会设置为is null
  • 若想忽略map中value为null的元素,可以在调用allEq时,设置参数boolean null2IsNull为false
@Test
public void test3() {QueryWrapper<User> wrapper = new QueryWrapper<>();Map<String, Object> param = new HashMap<>();param.put("age", 40);param.put("name", null);wrapper.allEq(param);List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);
}

6. lambda条件构造器

  • lambda条件构造器,支持lambda表达式,可以不必像普通条件构造器一样,以字符串形式指定列名,它可以直接以实体类的方法引用来指定列。
  • 像普通的条件构造器,列名是用字符串的形式指定,无法在编译期进行列名合法性的检查,这就不如lambda条件构造器来的优雅。
@Test
public void testLambda() {LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.like(User::getName, "黄").lt(User::getAge, 30);List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);
}- `like(User::getName, "黄")`:表示查询 `User` 表中 `name` 字段包含"黄"的记录。
- `lt(User::getAge, 30)`:表示查询 `User` 表中 `age` 字段小于 30 的记录
更新操作
  • updateById(T entity)
    根据入参entity的id(主键)进行更新,对于entity中非空的属性,会出现在UPDATE语句的SET后面,即entity中非空的属性,会被更新到数据库,示例如下
	@Testpublic void testUpdate2() {User user = new User();user.setId(1L); // 假设要更新的用户 IDuser.setName("新的名字"); // 只更新名字,其他属性保持不变userMapper.updateById(user);}
  • update(T entity, Wrapper<T> wrapper)
    根据实体entity和条件构造器wrapper进行更新,示例如下
	@Testpublic void testUpdate2() {User user = new User();user.setName("abc");LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();wrapper.between(User::getAge, 26,31).likeRight(User::getName,"吴");userMapper.update(user, wrapper);}
删除操作

BaseMapper一共提供了如下几个用于删除的方法

  • deleteById 根据主键id进行删除
  • deleteBatchIds 根据主键id进行批量删除
  • deleteByMap 根据Map进行删除(Map中的key为列名,value为值,根据列和值进行等值匹配)
  • delete(Wrapper<T> wrapper) 根据条件构造器Wrapper进行删除

7. 自定义Sql

  • 注解
	@Select("select * from user")List<User> selectRaw();
  • xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mp.mappers.UserMapper"><select id="selectRaw" resultType="com.example.mp.po.User">SELECT * FROM user</select>
</mapper>public interface UserMapper extends BaseMapper<User> {List<User> selectRaw();
}

8. 分页插件

  • 配置分页插件,在 MybatisPlusConfig 类中添加分页插件配置。
    @Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbTypereturn interceptor;}
  • 在查询时使用 Page 对象进行分页。
    @Testpublic void testPage() {LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.ge(User::getAge, 28);// 设置分页信息, 查第3页, 每页2条数据Page<User> page = new Page<>(3, 2);// 执行分页查询Page<User> userPage = userMapper.selectPage(page, wrapper);System.out.println("总记录数 = " + userPage.getTotal());System.out.println("总页数 = " + userPage.getPages());System.out.println("当前页码 = " + userPage.getCurrent());// 获取分页查询结果List<User> records = userPage.getRecords();records.forEach(System.out::println);}

9. 代码生成器

  • MyBatis Plus 提供了代码生成器,可以根据数据库表自动生成实体类、Mapper 接口、Service 类和 Controller 类。
public class Generator {public static void main(String[] args) {FastAutoGenerator.create("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8","root", "").globalConfig(builder -> builder.author("aa").outputDir("your-path")).packageConfig(builder -> builder.parent("your-package").entity("entity").mapper("mapper").service("service").serviceImpl("service.impl").xml("mapper.xml")).strategyConfig(builder -> builder.entityBuilder().enableLombok()).templateEngine(new FreemarkerTemplateEngine()).execute();}
}

4.3 高级功能

1. 自动填充

通过在实体类里面的字段进行设置时间戳, create_time, update_time等字段需要自动填充

	@TableField(fill = FieldFill.INSERT) // 插入时自动填充private long createTime;@TableField(fill = FieldFill.INSERT_UPDATE) // 插入/更新时自动填充private long updateTime;

自定义类 MetaObjectHandler 实现 MetaObjectHandler接口,并且去重写其两个方法:

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("开始插入填充...");this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());}@Overridepublic void updateFill(MetaObject metaObject) {log.info("开始更新填充...");this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}
}

2. 乐观锁(并发的情况)

乐观锁是一种并发控制机制,用于确保在更新记录时,该记录未被其他事务修改。MyBatis-Plus 提供了 OptimisticLockerInnerInterceptor 插件,使得在应用中实现乐观锁变得简单。

乐观锁的实现原理包括以下步骤:

  1. 读取记录时,获取当前的版本号(version)。
  2. 在更新记录时,将这个版本号一同传递。
  3. 执行更新操作时,设置 version = newVersion 的条件为 version = oldVersion。
  4. 如果版本号不匹配,则更新失败。

配置乐观锁插件:

    @Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}

Entity对象version字段加上@version注解

import com.baomidou.mybatisplus.annotation.Version;@Data
public class User {@TableIdprivate Long id;private String name;private Integer age;private String email;@Versionprivate Integer version;
}

http://www.mrgr.cn/news/99079.html

相关文章:

  • jangow靶机笔记(Vulnhub)
  • 使用注解方式整合ssm时,启动tomcat扫描不到resource下面的xxxmapper.xml
  • 基于spring boot 集成 deepseek 流式输出 的vue3使用指南
  • scikit-learn初探
  • 2024年国考
  • 信息量、香农熵、交叉熵、KL散度总结
  • 运筹学之遗传算法
  • 【音视频】音视频FLV合成实战
  • 大模型微调项目实战(情绪对话模型-数据工程篇)
  • 论文阅读:2022 ACL TruthfulQA: Measuring How Models Mimic Human Falsehoods
  • Java Web 之 Tomcat 100问
  • 移动自动化测试-appium
  • Windows使用SonarQube时启动脚本自动关闭
  • leetcode 674. Longest Continuous Increasing Subsequence
  • Unity webgl 获取图片或视频数据
  • leetcode 300. Longest Increasing Subsequence
  • AI分析师
  • 爬虫入门学习
  • 计算机网络八股——HTTP协议与HTTPS协议
  • 杨校老师课堂之C++入门练习题梳理