【Spring Boot】玩转基础 (一篇就够了)

 

目录

 资源

 项目地址

PS

一、新建 SpringBoot 项目

1.我这里连接了码云仓库

2.新建项目

2.1不用码云的的创建方式

 2.2使用码云的创建方式

3.使用 Spring InitiaIizr 创建项目

4.选择基本 Dependencies 依赖项

5.设置项目与文件编码格式 UTF-8

6.观察我们的项目架构

7.检查 JDK 没有正确在IDEA配置的问题(没有的则跳过)

8.测试运行时报异常(这是因为我们用了MyBatis依赖)

8.1解决方式一(配置连接MySQL)

 8.2解决方式二(忽略扫描装载和数据库有关的东西)

9.编写测试 SpringBoot 内嵌 Tomcat 服务

9.1 解决重定向到 /login 问题

9.2 低级 404  问题

9.3 服务运行成功

二、SpringBoot 配置&注解

 1. 配置文件

1.1MySQL 配置

2. pom.xml

三、MyBatis CRUD MySQL

0. PS

1. 检查准备配置

1.1检查并删除禁用数据库扫描装载(没有的不用动)

1.2配置MySQL数据库连接

1.3确保MySQL有这个数据库

1.4在springboot数据库中创建一张用户表,用于CRUD操作

2. Mapper 使用注解操作MySQL

2.1创建Bean类

2.2 @Mapper 注解创建 Mapper 接口

2.3 在测试类中调用UserMapper对象并运行测试类

2.4 解决实体类与数据表字段命名(驼峰与下划线)的映射

3. Mapper XML配置操作MySQL

3.1 配置 MyBatis XML映射文件路径

3.2 创建 Mapper 接口文件

3.3 创建XML SQL 映射文件

3.4 另外创建测试类,测试 MyBatis XML

4. Spring Boot 整合 JPA 操作 MySQL

4.1 在 pom.xml 中引入 JPA 依赖

4.2 配置 Bean User 实体类JPA注解

4.3 编写接口继承实现接口 JpaRepository,自定义查询语句,>

4.4 编写测试类,对JPA 接口自带CRUD方法操作数据表进行测试

4.5 自定义 CRUD SQL语句

4.5.1 编写测试类,进行JPA自定义SQL语句操作MySQL

四、Redis 缓存(铺垫) | 编写接口 | 视图技术(Thymeleaf)

0. PS

1. 编写接口 (控制器)

2. 视图技术(Thymeleaf)

2.1 我们需要在 pom.xml 中引入依赖

2.2 在全局配置文件 application.propertis 中配置

2.3 编写模板 HTML 

3. 运行 Spring Boot 并测试(学习编写接口和视图技术)

五、基于注解的 Redis 缓存(优化查询)

PS

1. 开启 Spring Boot 缓存管理注解支持

2. Spring Boot 自带缓存 Simple

3. 开启 JPA SQL 语句操作数据库时打印控制台

4. 编写 service 服务层控制 UserRepository 接口 (缓存逻辑)

5. 编写接口(控制器)访问接口来操作数据库

6. 体验无缓存 SQL 查询操作

6.1 我们把 UserService 服务类中的 getUserList() 方法上的 @Cacheable 注解先注释掉 :

6.2 运行我们的 Spring Boot 项目,然后打开我们的浏览器进行访问接口

6.3 查看 IDEA 控制台,多次刷新浏览器查询用户列表

7. 体验缓存 SQL 查询操作

7.1 我们把 UserService 服务类中的 getUserList() 方法上的 @Cacheable 注解的注释进行还原 :

7.2 正式 CRUD 体验缓存逻辑

7.3 体验缓存更新

8. 缓存小总结

9. 引入 Redis 缓存转变

9.1 添加 Redis 依赖

9.2 配置 Redis 密码 (可跳过)

9.3 配置文件连接 Redis

9.4 Redis 对 Bean 实体类实现序列化接口

9.5 体验 Redis 缓存

六、jar & war 打包及部署

0. 配置 pom.xml 打包规则

0.1 Maven 打包插件

 0.2 配置打包方式

0.3 配置可使用外部 Tomcat 部署

0.4 配置打包输出命名 (项目名+版本号)

0.5 完整 pom.xml 配置

1. 打包跳过测试文件

2. Jar 打包部署

2.1 配置 jar 打包方式

2.2 打包

2.3 打包报<编码UTF-8的不可映射字符>解决 (如没有可跳过)

​编辑

2.4 命令运行 Jar 包

3. War 打包 Tomcat 部署

3.1 配置 war 打包方式

3.2 主程序入口继承 SpringBootServletInitializer 类

3.4 打包

4. Tomcat 部署 war 包 (Windows)

4.1 安装 Tomcat

4.2 配置 Tomcat 环境变量

4.3 运行 Tomcat

4.4 浏览器访问项目

4.5 ROOT 根路径去除定义的文件夹名头访问链接


 资源

 百度网盘 提取码:1024

 项目地址

码云:Spring Boot Demo​​​​​​​

PS

Spring Boot 的主要优点包括:

  • 简化配置。它采用约定优于配置的原则,提供了自动配置功能,可以减少开发人员的大量手动配置工作。
  • 快速开发。它提供了快速开发的工具和功能,如热部署、自动重载、自动刷新,以及内嵌的Web服务器,如TomcatJettyUndertow,简化了部署过程。
  • 微服务支持。它支持微服务架构,提供了一系列功能和工具,如服务发现、负载均衡、熔断器等,帮助构建和管理微服务应用程序。
  • 为SpringCloud微服务奠定基础,使微服务构建变得简单

一、新建 SpringBoot 项目

  • 方式一:使用 Maven 创建 SpringBoot 项目
  • 方式二:使用 Spring InitiaIizr 创建SpringBoot 项目

第二种方式更为简便,所以这里直接用第二种方式 :

  • JDK 19
  • IDEA 2021
  • Spring Boot 3.2.3

Spring Boot 支持的 JDK 版本简述

Spring Boot 版本 2.4.x 及之前的版本都支持 JDK 8。这意味着,如果你使用 JDK 8,你可以选择 Spring Boot 2.4.x 或之前的版本来构建应用程序。不过,从 Spring Boot 2.5.0 开始,官方不再支持 JDK 8,最低要求提升到 JDK 11。

1.我这里连接了码云仓库

2.新建项目

2.1不用码云的的创建方式

PS:File ——> New ——> Project

 2.2使用码云的创建方式

PS:项目右键 ——> New ——> Module

3.使用 Spring InitiaIizr 创建项目

  • Server URL:start.spring.io ,默认
  • Name:项目名
  • Location:项目本地保存路径
  • Language :使用的编程语言
  • Type:使用的依赖仓库管理类型,选 Maven
  • Group:组 id,会配置到 pom.xml
  • Artifact:定位名,会配置到 pom.xml
  • Package name:包名,跟着源码包名一起创建。
  • Project SDK:JDK 版本。
  • Java:使用的具体编译 Java 版本,会配置到 pom.xml。

4.选择基本 Dependencies 依赖项

5.设置项目与文件编码格式 UTF-8

PS:File ——> Settings ——> Editor ——> File Encodings 。

Project Encoding 选项选 GBK,其它都是 UTF-8 编码。

控制台也设置一下,要不然中文可能乱码:

PS:File ——> Settings ——> Editor ——> General ——> Console 。

Default Encoding 选项选择 UTF-8 编码。

6.观察我们的项目架构

7.检查 JDK 没有正确在IDEA配置的问题(没有的则跳过)

8.测试运行时报异常(这是因为我们用了MyBatis依赖)

8.1解决方式一(配置连接MySQL)

***************************
APPLICATION FAILED TO START
***************************

Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
    If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
    If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).

 请更改为你定义的数据库名(YouMySQLDataName)和数据库用户名和密码

spring.datasource.url=jdbc:mysql://localhost:3306/YouMySQLDataName
spring.datasource.username=root
spring.datasource.password=123456

 8.2解决方式二(忽略扫描装载和数据库有关的东西)

在注解@SpringBootApplication的程序入口类,禁止

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

9.编写测试 SpringBoot 内嵌 Tomcat 服务

右击DemoApplication启动运行,等待运行成功后,在浏览器地址栏输入

http://localhost:8080/hello

出现错误,发现地址重定向到 /login :

9.1 解决重定向到 /login 问题

原因分析

  • 依赖中加入了spring security,SpringSecurity的依赖
  • SecurityAutoConfiguration 是Spring Boot提供的安全自动配置类(也就是说它自动集成了SpringSecurity)

解决:在注解@SpringBootApplication的程序入口类,加入禁止自动配置

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})

因为我们添加了数据库依赖,根据前面忽略数据库的也可以先加入,后面CRUD需要删除忽略数据库的加载类

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, SecurityAutoConfiguration.class})

9.2 低级 404  问题

再次运行报 404 问题

原因分析

  • SpringBoot 默认只会扫描自动装载与 @SpringBootApplication 注解启动入口同包下的所有文件或所有子文件。如启动类在com包下,则会扫描当前com包下所有文件和子文件。
  • 自动装配的注解包括@Controller@RestController、@Service@Component@Repository等等。
  • 我这里的启动类和@RestController注解类并不是在同一个包下。

解决404问题

  1. 可以把启动类放到 com 包下。
  2. 使用 @ComponentScan 注解指定扫描包、文件、自定义类扫描器。

1. 推荐采用第一种方式,直接将启动类拖到com包下

2.第二种方式,指定扫描包,而不是把启动类放到com包下扫描全部,在大型项目中多了就慢。

@ComponentScan(value = "com")

注解 @ComponentScan 是可以定义多个的,我这里定义多余了,这里只要随便一个就行

9.3 服务运行成功

二、SpringBoot 配置&注解

 1. 配置文件

全局配置文件:  src/main/resources/
  • Spring Initializr 自动生成:application.properties
    • 语法:(使用点分制)
          主配置名.子配置名.子配置名=配置项或值
  • 手动创建全局配置文件: application.yaml 或 .yml 文件格式
    • 主配置名:(冒号)回车
          子配置名:(冒号)空格 配置项或值

1.1MySQL 配置

spring.datasource.url=jdbc:mysql://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=123456
属性对象值:解释
spring.datasource.url

jdbc:mysql://localhost:3306/springboot

是一种url协议的一种,使用jdbc连接mysql,3306则是端口,

springboot 是连接的数据库名

通常数据库名后,可以加get请求的参数如:jdbc:mysql://localhost:3306/springboot?encoding=utf-8

没有特殊需求默认就好,因为编码默认就是utf-8

spring.datasource.username数据库用户名
spring.datasource.password数据库密码

2. pom.xml

PS:用于配置我们要使用的依赖库,一般如:

<dependencies>
    <!-- Spring Data Redis依赖启动器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--    Spring DAta JPA 依赖启动器    -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

</dependencies>

依赖标签解释
dependencies编写依赖的内容
dependency每个依赖的个体
groupId此依赖所属组ID
artifactId依赖ID

说了跟说一样

三、MyBatis CRUD MySQL

0. PS

这里展示最简单的三种方式

  1. 注解的方式,配合SQL语句。
  2. XML配置文件的方式,在配置文件中编写SQL,配置文件的方式必须得掌握,在开发中可以很好的分离操作SQL语句。
  3.  JPA框架,是第一种方式的延申,简单来说有很多基本SQL语句不需要写,极大的简化的代码量。

1. 检查准备配置

PS:注解的方式操作只需要在接口类注解,且需要Bean对象即可,(提前在MySQL创建一个数据库,以及数据表)

编写前的准备工作

1.1检查并删除禁用数据库扫描装载(没有的不用动)

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})

1.2配置MySQL数据库连接

在全局配置文件 application.properties 中

 root为用户名,123456为密码,设置成你自己设置的

spring.datasource.url=jdbc:mysql://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=123456

1.3确保MySQL有这个数据库

这里用Navicat工具连接数据库并创建springboot数据库

1.4在springboot数据库中创建一张用户表,用于CRUD操作

注意:这里的id是字段是设置为自动递增的,看参数设置,最后保存为 t_user 表名

2. Mapper 使用注解操作MySQL

2.1创建Bean类

在com包下新建bean包,创建User类,用于 Srping boot 自动创建或解析的实体对象

import lombok.Data;
import org.springframework.stereotype.Component;

@Data
@Component
public class User {

    private Integer id;
    private String userName;
    private String pwd;

}

2.2 @Mapper 注解创建 Mapper 接口

在com包下新建mapper包,并创建UserMapper接口操作数据:

import com.bean.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

@Mapper
public interface UserMapper {

    @Insert("INSERT INTO t_user(id, user_name, pwd) values(#{id}, #{userName}, #{pwd})")
    int insertUser(User user);

    @Select("SELECT * FROM t_user")
    List<User> getAllUser();

    @Delete("DELETE FROM t_user WHERE id = #{id}")
    int deleteUserById(Integer id);

    @Update("UPDATE t_user SET pwd = #{pwd} WHERE user_name = #{userName}")
    int updatePasswordByUserName(User user);

}

2.3 在测试类中调用UserMapper对象并运行测试类

MybatisTests

import com.bean.User;
import com.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MybatisTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void insertUser() {
        User user = new User();
        user.setUserName("bin");
        user.setPwd("bin");
        int resposeCode =  userMapper.insertUser(user);
        System.out.println("响应码:" + resposeCode);
    }

    @Test
    public void selectUserList() {
        for (User user : userMapper.getAllUser()) {
            System.out.println(user);
        }
    }

}

2.4 解决实体类与数据表字段命名(驼峰与下划线)的映射

在全局配置文件中配置

#解决实体类与数据表字段命名(驼峰与下划线)的映射
mybatis.configuration.map-underscore-to-camel-case=true

再次运行 MybatisTests 测试类,解决问题

3. Mapper XML配置操作MySQL

3.1 配置 MyBatis XML映射文件路径

PS:我们在编写XML映射文件后,Spring Boot 并无从知晓,所以无法扫描到该自定义编写的XML配置文件,还必须在全局配置文件 application.properties 中添加MyBatis 映射文件路径配置,同时需要添加实体类别名映射路径。

XML 配置文件写到 resources,后期我们在 resources 下创建 mapper 文件夹存放 XML Mapper文件

#配置 MyBatis 的XML配置文件路径, resources:
mybatis.mapper-locations=classpath:mapper/*.xml
#配置XML映射文件中指定的实体类别名路径
mybatis.type-aliases-package=com.bean

3.2 创建 Mapper 接口文件

在 com.mapper 包下创建 UserXMLMapper 接口:

import com.bean.User;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface UserXMLMapper {

    int insertUser(User user);
    List<User> getAllUser();
    int deleteUserById(Integer id);
    int updatePasswordByUserName(User user);

}

3.3 创建XML SQL 映射文件

在 resources 下创建 mapper 映射的文件夹,之前在全局配置文件中配置过,必须对应名字,以及对应的Bean 实体类映射全别名也需要是指定位置

在 resources 资源文件夹的 mapper 文件夹下新建 XML 文件:

XML文件名可以自己定义,最好统一和Mapper接口文件一直名字,UserXMLMapper

<?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.mapper.UserXMLMapper">
    <!-- 插入 -->
    <insert id="insertUser">
        INSERT INTO t_user (user_name, pwd) values (#{userName}, #{pwd})
    </insert>
    <!-- 查询 -->
    <select id="getAllUser">
        SELECT * FROM t_user
    </select>
    <!-- 删除 -->
    <delete id="deleteUserById">
        DELETE FROM t_user WHERE id = #{id}
    </delete>
    <!-- 更新 -->
    <update id="updatePasswordByUserName">
        UPDATE t_user SET pwd = #{pwd} WHERE user_name = #{userName}
    </update>
</mapper>

解释

  • mapper 标签为主标签。
  • namespace 属性:翻译是命名空间的意思,也就是指定对应的Mapper接口文件全限定名。
  • 二级子标签:一般对应要写的语句类型即可。
  • id 属性:对应指定的命名空间Mapper接口文件中的方法名。

3.4 另外创建测试类,测试 MyBatis XML

MybatisXMLTests

import com.bean.User;
import com.mapper.UserXMLMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MybatisXMLTests {

    @Autowired
    private UserXMLMapper userXMLMapper;

    @Test
    public void insertUser() {
        User user = new User();
        user.setUserName("bin");
        user.setPwd("bin");
        int resposeCode = userXMLMapper.insertUser(user);
        System.out.println(resposeCode);
    }

    @Test
    public void selectUserList() {
        for (User user : userXMLMapper.getAllUser()) {
            System.out.println(user);
        }
    }

}

运行 MybatisXMLTests 测试类 IDEA控制台输出结果和SQL数据库:

4. Spring Boot 整合 JPA 操作 MySQL

4.1 在 pom.xml 中引入 JPA 依赖

        <!-- JPA -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>

 IDEA刷新 maven 依赖仓库

4.2 配置 Bean User 实体类JPA注解

为了方便,直接在已有的 User 类中进行修改 JPA 注解

  1. @Entity 注解:标记为 JPA Bean 类,注解在类名。
  2. @Table 注解:指定对应的数据库表名,注解在类名。
  3. @Id 注解:在数据表表中 自动递增的键字段,注解在成员变量,(在指定了表名后必须指定的注解)。
  4. 省略 @Table 注解 :在 @Entity 注解中直接设置表名 @Entity(name = "table_name"),而不需要 @Table(name = "table_name") 。
  5. @GeneratedValue(strategy= GenerationType.IDENTITY) :主键生成策略。
  6. @Column 注解:@Column(name = "对应数据表列名") 数据表列名映射关系。
import jakarta.persistence.*;
import lombok.Data;
import org.springframework.stereotype.Component;

@Data
@Component
//@Entity
//@Table(name = "t_user")
@Entity(name = "t_user")
public class User {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY) //配置主键的生成策略
    @Column(name = "id")
    private Integer id;

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

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

}

4.3 编写接口继承实现接口 JpaRepository<BeanType, IndexType>,自定义查询语句

  • JpaRepository<BeanType, IDType> :bean 为操作数据表的实体类型,Id 为主键索引类型。

PS吐槽:这里编写接口继承,如果实在点说其实算是实现接口。

在com包下新建repository包并建立接口UserRepository:

import com.bean.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Integer> {
    //这里直接省略,自定义的查询
}

4.4 编写测试类,对JPA 接口自带CRUD方法操作数据表进行测试

MybatisJPATests

import com.bean.User;
import com.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class MybatisJPATests {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void insertUser() {
        User user = new User();
        user.setUserName("bin");
        user.setPwd("bin");
        User saveUser = userRepository.saveAndFlush(user); //插入直接用保存并刷新方法
        System.out.println(saveUser);
    }

    @Test
    public void findAll() {
        List<User> list = userRepository.findAll(); //查询全部
        for (User user : list) {
            System.out.println(user);
        }
    }

    @Test
    public void deleteById() {
        userRepository.deleteById(999); //根据表主键id进行删除
    }

    @Test
    public void update() {
        User user = new User();
        user.setId(9); //对象中通过ID逐渐区分对象,进行更新用户名或密码(成员)
        user.setUserName("bin");
        user.setPwd("updateBinPwd");
        userRepository.saveAndFlush(user); //更新其实就是保存方法,只不过ID值需要指定
    }

}

注意

  • 根据数据库设置的键值唯一性质,saveAndFlush()保存方法和插入方法一致,只不过在更新时指定键值。
  • 有些方法没有返回的结果码。

运行 MybatisJPATests 测试类 ,仅执行 insertUser() 方法再执行 updateUserById() 方法的执行结果:

4.5 自定义 CRUD SQL语句

直接修改接口 UserRepository

import com.bean.User;

import org.apache.ibatis.annotations.Param;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Transactional //直接注解在接口上,确保事物完整性
public interface UserRepository extends JpaRepository<User, Integer> {

    @Modifying
    @Query(nativeQuery = true, value = "INSERT INTO t_user (user_name, pwd) values (:#{#user.userName}, :#{#user.pwd})")
    int insertUser(@Param("user") User user);

    @Query(nativeQuery = true, value = "SELECT * FROM t_user")
    List<User> getAllUser();

    @Modifying
    @Query("DELETE FROM t_user WHERE id = :id")
    int deleteUserById(@Param("id") Integer id);

    @Modifying
    @Query(nativeQuery = true, value = "UPDATE t_user SET user_name = :#{#user.userName}, pwd = :#{#user.pwd} WHERE id = :#{#user.id}")
    int updatePasswordById(@Param("user") User user);
}

注解:

  • @Transactional :事物机制,确保数据在增删改的正确性。(可以注解在接口上,也可以注解在方法上)。
  • @Modifying :注解为增删改 SQL 语句方法。
  • @Query :意为查询语句
    • nativeQuery = true :使用原生 SQL 语句。
    • value = "" :编写SQL语句的地方。
  • @Param :对参数属性进行设置 @Query 注解中SQL语句调用的变量名,在SQL中调用时使用英文冒号调用   :param
  • 不用 @Param 注解对参数别名 :可以使用序号调用,从1~N 的参数个数,在@Query注解中调用时使用英文问号调用 ?1 或 ?2 或 ?3,序号对应这声明参数时的位置,最小从1开始。注意的是在@Query注解中调用时不能把序号弄反,如:@Query("?3 ?1 ?2"),这是不允许的,调用顺序必须跟着参数声明的顺序!(PS: 这种方式我并不推荐,在后期维护时容易混乱)。
  • SpEL 表达式调用实体类参数:格式 :#{#param.属性名}

4.5.1 编写测试类,进行JPA自定义SQL语句操作MySQL

MybatisJPA_DIY_SQL_Tests

import com.bean.User;
import com.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class MybatisJPA_DIY_SQL_Tests {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void inserUser() {
        User user = new User();
        user.setUserName("binJpa");
        user.setPwd("binJpa");
        int resultCode = userRepository.insertUser(user);
        System.out.println(resultCode);
    }

    @Test
    public void selectAllUser() {
        List<User> list = userRepository.getAllUser();
        for (User user : list) {
            System.out.println(user);
        }
    }

    @Test
    public void deleteById() {
        int resultCode = userRepository.deleteUserById(999);
        System.out.println(resultCode);
    }

    @Test
    public void updateUserById() {
        User user = new User();
        user.setId(12);
        user.setUserName("binJpa");
        user.setPwd("updateBinJpa");
        int resultCode = userRepository.updatePasswordById(user);
        System.out.println(resultCode);
    }

}

运行 MybatisJPA_DIY_SQL_Tests 测试类,仅执行 insertUser() 方法再执行 updateUserById() 方法的执行结果:

四、Redis 缓存(铺垫) | 编写接口 | 视图技术(Thymeleaf)

0. PS

注意:仅为第五节做铺垫。

理解程序在内存中的缓存,以及在读取后写入缓存,当写入缓存后,在数据库这些数据变化后,我们的缓存必须被更新或者删除问题,否则可能会出现缓存BUG。Spring Boot 有默认自带缓存,以及第三方允许的缓存框架依赖程序,其实我们的Spring Boot从自带缓存,到依赖程序缓存的转换,Spring Boot都会自动检测定位使用哪种缓存程序。

为了更方便的测试出效果,我们采用如下方法

  • 首先编写接口(控制器),需要的注解了解,以及请求参数的获取映射。
  • 视图技术,使用 Thymeleaf 依赖,主要用于编写网页模板测试GET和POST请求参数的区别。(这里不涉及 Thymeleaf 表达式的讲解)
  • 运行 Spring Boot ,通过浏览器来访问接口来操作数据库,操作数据使用 JPA 方式,我们在IDEA 控制台中可以查看 Redis SQL语句查询执行显示,来观察 Redis 是否成功运用而避免缓存BUG。

1. 编写接口 (控制器)

在 com.controller 包下创建控制器类 ThymeleafController

import com.bean.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;

@Controller //指定控制器类
@RequestMapping("/test") //指定当前类控制器全局地址,之后在每个(方法)接口上都是连接着这个主路径
public class ThymeleafController {

    @GetMapping("") //设置为根路径,进入 Thymeleaf 视图技术使用的 test.html 模板测试之后的 GET 请求和 POST 请求方法
    public String testIndex() {
        //如果且当有的情况,返回的是 resources/templates 静态资源下的 .html 文件名称
        return "thymeleaf";
    }

    //————————— 测试方法 <链接参数与请求体参数> 定义参数获取问题 —————————>

    @GetMapping("/get") //测试设置了 request 和 response 对象参数
    public void handleGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.getWriter().println(request.toString()); //打印请求对象,仅作测试
        response.getWriter().println("Test method params HttpServletRequest & HttpServletResponse");
    }

    @GetMapping("/get/{userName}/{pwd}") 
      //通过括号 {} 的方式拿取 URL 设置 <路径> 参数,@PathVariable 注解传值映射到方法中
//    public void testGetPath(@PathVariable("userName") String userName, @PathVariable("pwd") String pwd, HttpServletResponse response) throws IOException {
//        response.getWriter().println("userName == " + userName + "\n" + "pwd == " + pwd);
//    }
    //通过 @ModelAttribute 把路径参数属性映射到实体类上
    public void testGetPath(@ModelAttribute User user, HttpServletResponse response) throws IOException {
        response.getWriter().println("userName == " + user.getUserName() + "\n" + "pwd == " + user.getPwd());
    }

    //测试 @RequestParam 注解拿取 URL | <请求体 Body> 中的请求参数 (非 JSON 格式)
    @RequestMapping(value = "/params", method = {RequestMethod.GET, RequestMethod.POST}) //设置可接收两种请求方法
    public void requestParams(@RequestParam("param1") String param1, @RequestParam("param2") String param2, HttpServletResponse response) throws IOException {
        response.getWriter().println("param1 == " + param1 + "\n" + "param2 == " + param2);
    }

    //测试 无注解参数,SpringBoot 自动将请求参数 (非 JSON 格式) 映射到实体类中
    @RequestMapping(value = "/instance1", method = {RequestMethod.GET, RequestMethod.POST}) //设置可接收两种请求方法
    public void postInstance1(User user, HttpServletResponse response) throws IOException {
        response.getWriter().println(user.toString());
    }

    //post请求测试 @RequestBody 注解将请求体 JSON 参数映射到实体类中
    @PostMapping(value = "/instance2", consumes = {"application/json"}) //设置只接收 json 格式数据
    public void postInstance2(@RequestBody User user, HttpServletResponse response) throws IOException {
        response.getWriter().println(user.toString());
    }

}

注解解释

  • 定义控制器类:
    • @RestController :它将类中的方法的返回值直接转换为 JSON 或 XML (HTML),并返回给客户端。
    • @Controller:它将类中的方法的返回值返回纯文本数据格式。
      • 特殊条件:在这里使用了视图技术,需要使用 @Controller 注解才能引导检索到静态模板页面文件,也就是方法的返回值字符串当成定位到 Thymeleaf 模板页面的 路径字符串,并返回给客户端。
  • @RequestMapping:设置请求 URL 地址
    • 定义在类上时:当前类中所有的方法上请求的接口链接都隶属于类上的。
    • 定义在方法上:定义设置的接口地址处理的逻辑。
    • 在一个方法上允许多种请求协议时:必须使用 @RequestMapping ,而不是在一个方法上定义多个其它的 Mapping 。
  • 请求接口路径(定义在处理方法上):
    • @GetMapping:为 GET 协议,设置请求 URL 地址,规范用于请求数据。
    • @PostMapping:为 POST 协议,设置请求 URL 地址,规范用于提交数据。
    • @PutMapping:为 PUT 协议,设置请求 URL 地址,规范用于更新数据。
    • @DeleteMapping:为 DELETE 协议,设置请求 URL 地址,规范用于删除数据。
  • value :URL 接口地址,当只有一个字符串 URL 地址时,无需显示设置 value。
  • method:可以同时允许当前 URL 处理逻辑多种请求方法。
  • consumes:设置接受的请求参数内容。
  • 获取请求参数(定义在方法参数上):
    • @PathVariable:用于获取 URL 请求路径括号 {} 内设置的表达式中的变量名传递数据映射到方法参数上。
    • @ModelAttribute:直接将路径请求参数映射到实体类上。
    • @RequestParam:用于获取 URL 或者 请求体 Body 中的键值对数据非 JSON 数据),通过设置的请求参数键名获取并传递映射到方法参数上。
    • @RequestBody:用于将请求体中 JSON 数据 映射到实体类Bean对象上。

2. 视图技术(Thymeleaf)

2.1 我们需要在 pom.xml 中引入依赖

PS:如果有就不用了

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

引入后别忘了刷新 maven 仓库:

2.2 在全局配置文件 application.propertis 中配置

#———————— Thymeleaf 模板配置 ——————————
#启用模板缓存,默认开启 true,开发中方便调试设置为 false ,上线后还原为 true
spring.thymeleaf.cache=false
#模板页面编码,默认 UTF-8
#spring.thymeleaf.encoding=UTF-8
#模板页面模式,默认 HTML5
#spring.thymeleaf.mode=HTML5
#模板页面名称后缀,默认 .html
#spring.thymeleaf.suffix=.html

2.3 编写模板 HTML 

resources 资源文件夹下创建 templates 文件夹,然后在 templates 下创建 thymeleaf.html HTML文件:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf</title>
</head>
<body>
    <div align="center">
        <p>接口:/test/get   协议:GET</p>
        <form action="/test/get" method="get">
            <input type="submit" value="测试跳转">
        </form>
    </div>
    <hr>
    <div align="center">
        <p>接口:/test/get/{userName}/{pwd}    协议:GET</p>
        <form action="/test/get/DIY_userName/DIY_pwd" method="get">
            <input type="submit" value="路径方式传值">
        </form>
    </div>
    <hr>
    <div align="center">
        <p>接口:/test/params   协议:GET</p>
        <form action="/test/params" method="get">
            <div>
                <input type="text" name="param1"><br>
                <input type="text" name="param2">
            </div>
            <input type="submit" value="GET请求参数传值">
        </form>
    </div>
    <div align="center">
        <p>接口:/test/params   协议:POST</p>
        <form action="/test/params" method="post">
            <div>
                <input type="text" name="param1"><br>
                <input type="text" name="param2">
            </div>
            <input type="submit" value="POST请求体参数传值">
        </form>
    </div>
    <hr>
    <div align="center">
        <p>接口:/test/instance1   协议:POST</p>
        <form action="/test/instance1" method="post">
            <div>
                <input type="text" name="userName"><br>
                <input type="text" name="pwd">
            </div>
            <input type="submit" value="非JSON数据解析映射Bean对象">
        </form>
    </div>
    <script type="text/javascript">
        function submitJSON() {
            const userName = document.getElementById("userName").value;
            const pwd = document.getElementById("pwd").value;
            const params = JSON.stringify({
                userName : userName,
                pwd : pwd
            });
            const ajax = new XMLHttpRequest();
            ajax.open('POST', 'http://localhost:8080/test/instance2', true);
            ajax.setRequestHeader('Content-Type', 'application/json'); //数据内容
            ajax.onreadystatechange = () => {
                if (ajax.readyState === XMLHttpRequest.DONE) { //请求完成
                    if (ajax.status === 200) {
                        console.log(ajax.responseText); //处理服务器响应
                    } else {
                        console.error('请求失败: ' + ajax.status); //处理请求失败的情况
                    }
                }
            };
            ajax.send(params); //传递参数发送请求
        }
    </script>
    <div align="center">
        <p>接口:/test/post/instance2   协议:POST  PS: 响应数据打印在控制台</p>
        <div>
            <input type="text" id="userName"><br>
            <input type="text" id="pwd">
        </div>
        <input type="button" value="JSON数据解析映射Bean对象" onclick="submitJSON()">
    </div>
</body>
</html>

 我:为了更好的观察体验,直接不优化抽象代码,直观看个体即可(我的借口)!

<html lang="en" xmlns:th="http://www.thymeleaf.org"> :引入 Thymeleaf 命名空间。th:text用于处理p标签体的文本内容。该模板文件直接在任何浏览器中正确显示,浏览器会自动忽略它们不能理解的属性th:text。但这不是一个真正有效的 HTML5HTML5HTML5 文档,因为 HTML5 规范是不允许使用th:*这些非标准属性的。我们可以切换到 Thymeleaf 的data-th-*语法,以此来替换th:*语法

简单来说就是可以使用 Thymeleaf 表达式,但这里不使用

3. 运行 Spring Boot 并测试(学习编写接口和视图技术)

浏览器访问:(进入测试网页)

http://localhost:8080/test

返回 thymeleaf.html 网页

测试结果

五、基于注解的 Redis 缓存(优化查询)

PS

。。。。巴拉拉,根据以上第四节的认识,我们就快速开始进行 Redis 缓存的实验。

前面说了,Spring Boot 自带缓存到 依赖程序缓存的支持。

1. 开启 Spring Boot 缓存管理注解支持

在主入口 @SpringBootApplicatioon 的类上,加入如下注解

@EnableCaching //开启 Spring Boot 基于注解的缓存管理支持

2. Spring Boot 自带缓存 Simple

非常重点只要不引入和配置 Spring Boot 支持的缓存程序,会自动使用自带的缓存。相反,当引入和配置指定的缓存程序后,自动接入缓存程序!!!!

包含支持以下9种依赖缓存程序

  1. Generic
  2. JCache
  3. EhCache 2.x
  4. Hazeicast
  5. Infinispan
  6. Couchbase
  7. Redis
  8. Caffeine
  9. Simple :这个即为默认自带缓存,缓存使用的相关对象为 ConcuurentHashMap 。

PS:我们直接延用之前写的 JPA UserRepository 接口操作数据库,增加一个新的查询语句,因为集合对象对缓存操作有所弊端,所以增加一个单个实体查询的。

import com.bean.User;

import org.apache.ibatis.annotations.Param;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Transactional //直接注解在接口上,确保事物完整性
public interface UserRepository extends JpaRepository<User, Integer> {

    @Query(nativeQuery = true, value = "SELECT * FROM t_user")
    List<User> getAllUser();

    @Query(nativeQuery = true, value = "SELECT * FROM t_user WHERE id == #{id}}")
    User selectUserById(Integer id);

    @Modifying
    @Query(nativeQuery = true, value = "INSERT INTO t_user (user_name, pwd) values (:#{#user.userName}, :#{#user.pwd})")
    int insertUser(@Param("user") User user);

    @Modifying
    @Query("DELETE FROM t_user WHERE id = :id")
    int deleteUserById(@Param("id") Integer id);

    @Modifying
    @Query(nativeQuery = true, value = "UPDATE t_user SET user_name = :#{#user.userName}, pwd = :#{#user.pwd} WHERE id = :#{#user.id}")
    int updatePasswordById(@Param("user") User user);
}

3. 开启 JPA SQL 语句操作数据库时打印控制台

在全局配置文件 application.properties 中进行加入如下

#使用JPA进行数据库SQL查询语句时进行打印显示到控制台
spring.jpa.show-sql=true

4. 编写 service 服务层控制 UserRepository 接口 (缓存逻辑)

PS目的是后期我们在服务层进行管理控制缓存,无需改动原代码。

在 com 包下 新建 services 包下创建 UserService 服务类

  • @Service :标记类为业务层服务注解。

import com.bean.User;
import com.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@CacheConfig(cacheNames = UserService.mainName) //指定当前类全局缓存空间命名, 并自动开启当前服务类操作的对象全部SQL语句进行缓存管理
public class UserService {

    //定义静态缓存空间名属性
    public static final String mainName = "com.services.UserService";
    //—————————————————————————————— 特别注意 ————————————————————————————
    public static final String userListKey = "'userList'"; //当使用字符串时做key时, 需要使用单引号引起来

    @Autowired
    private UserRepository userRepository;

    //用户列表,unless 为 true 时则不进行缓存, #result 固定关键字为方法返回结果, 缓存的是返回对象
    @Cacheable(key = userListKey, unless = "#result == null")
    public List<User> getUserList() {
        return userRepository.getAllUser();
    }

    //通过 id 作为存储的键, unless 为 true 时则不进行缓存, #result 固定关键字为方法返回结果, 缓存的是方法返回的对象
    @Cacheable(key = "#id", unless = "#result == null") //缓存单个用户对象
    public User selectUserById(Integer id) {
        return userRepository.selectUserById(id);
    }

    //当插入新的对象, 用户列表缓存直接删除指定key列表, 无需方法返回值对比
    @CacheEvict(key = userListKey)
    public String insertUser(User user) {
        return user.toString() + "  插入结果码:" + userRepository.insertUser(user);
    }

    //更新 resCode 为 1 真的时候,才更新, 通过指定的 key 更新, 更新对象为方法返回值, 结果不为 null 时更新
    @CachePut(key = "#user.id", unless = "#result == null") //更新缓存单个用户对象
    @CacheEvict(key = userListKey) //当更新的对象,用户列表缓存直接删除
    public User updateUserById(User user) { //通过id进行更新行数据
        int resCode = userRepository.updatePasswordById(user);
        if (resCode != 1) user = null;
        return user;
    }

    //清除多个指定 key 的缓存数据, @CacheEvict 注解不会根据方法返回值判断
    //可同时指定缓存空间,但只有一个缓存空间在类设置了,所以无需设置
    @Caching(evict = {@CacheEvict(key = "#id"),
                      @CacheEvict(key = userListKey)})
    public String deleteUserById(Integer id) {
        return "删除id为 " + id + " 用户的结果码:" + userRepository.deleteUserById(id);
    }

}

缓存注解解释

注解描述示例
@EnableCaching
开启 Spring Boot 基于注解的缓存管理支持一般直接应用于主入口 @SpringBootApplication 类上
@Cacheable
  • 一般用于查询方法上
  • 作用于方法或类
  • 执行顺序:
    • 缓存查询
      • 空:查询数据库,并缓存。
      • 不为空:直接返回。
@CachePut
  • 用于更新缓存数据
  • 作用于方法或类
  • 执行顺序:
    1. 执行方法逻辑。
    2. 获取方法返回的结果进行缓存更新。
注意是通过方法的执行返回结果更新数据的
@CacheEvict
  • 用于删除缓存数据
  • 作用于方法或类
  • 默认执行顺序:
    1. 执行方法调用。
    2. 清除指定数据缓存。

特有属性:

  • allEntries
    • 默认值为 false,true 则表示清除指定的缓存空间全部缓存数据。
  • beforelnvocation
    • 默认为 false,true 则表示在执行方法前先删除数据(也就是调转执行顺序)。

清除指定空间全部缓存:

@CacheEvict(cacheNames = "空间名", allEntries = true)

@Caching
  • 整合 @Cacheable 、@CachePut、@CacheEvict 这三个注解的功能,并且还能指定多组注解。
  • 特有属性 (每种属性可以加入特定的注解,且可以加入多组,用英文逗号分开):
    • cacheable = {@Cacheable(),@Cacheable(),...}
    • put = {@CachePut(),@CachePut(),...}
    • evict = {@CacheEvict(),@CacheEvict(),...}
@Caching(put = {@CachePut(key="'k'")},evict = {@CacheEvict(key="'d'"))

@CacheConfig
  • 仅作用于类
  • 统一类中所有的以上缓存注解未指定 cacheNames 缓存空间属性值,用当前注解进行直接指定缓存空间名。
@CacheConfig(cacheNames = "com:main:caches")

缓存注解属性解释

属性名说明
cacheNames value指定缓存空间的名称,必配属性,二选一
key指定缓存数据的key,不设置就默认使用方法参数列表,(SpEL 表达式)
condition指定条件为 true 时,才对数据进行缓存,(SpEL 表达式)
unless指定条件为 true 时,不进行数据缓存,(SpEL 表达式)
sync对数据缓存时是否使用异步模式,默认 false
keyGenerator指定缓存数据的 key 生成器,与 key 属性二选一
cacheManager指定缓存管理器
cacheResolver指定缓存解析器,与 cacheManager 属性二选一

缓存注解属性SpEL表达式解释

名称描述示例
ArgumentName

当前方法参数列表,使用 # 号调用:

  • 索引方式(对照参数位置从 0 开始):
    • #a0、#a1、#a2 ... (#a 来调用)
    • #p0、#p1、#p2 ... (#p 来调用)
  • 参数名调用
    • #参数名
result当前方法返回的结果类型数据#result
caches当前被调用方法的缓存列表#root.caches[0].name
args当前被表用方法的参数列表#root.args[0]
methodName当前被调用的方法名#root.methodName
method当前被调用的方法#root.method.name
target当前被调用的目标对象实例#root.target
targetClass当前被调用的目标对象的类#root.targetClass

5. 编写接口(控制器)访问接口来操作数据库

为了方便,CRUD操作全部支持 GET 协议,之后通过浏览器进行接口访问

在 com.controller 包下创建 RedisJPA_API_Controller 控制器

package com.controller;

import com.bean.User;
import com.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.annotation.RequestScope;

import java.util.List;

@RestController
@RequestMapping("/api") //指定当前类控制器全局地址,之后在每个(方法)接口上都是连接着这个主路径
public class RedisJPA_API_Controller {

    @Autowired
    private UserService userService;

    @GetMapping("/get") //查询用户列表
    public List<User> getUserList() {
        return userService.getUserList();
    }

    @GetMapping("/get/{id}") //查询单个用户
    public User selectUserById(@PathVariable("id") Integer id) {
        return userService.selectUserById(id);
    }

    //插入用户, 规范使用 POST 协议
    @RequestMapping(value = "/insert/{userName}/{pwd}", method = {RequestMethod.GET, RequestMethod.POST})
    public String insertUser(@ModelAttribute User user) {
        return userService.insertUser(user);
    }

    //更新用户, 规范使用 PUT 更新协议
    @RequestMapping(value = "/update/{id}/{userName}/{pwd}", method = {RequestMethod.GET, RequestMethod.PUT})
    public User updateUserById(@ModelAttribute User user) {
        return userService.updateUserById(user);
    }

    //删除用户, 规范使用 DELETE 删除协议
    @RequestMapping(value = "/delete/{id}", method = {RequestMethod.GET, RequestMethod.DELETE})
    public String deleteUserById(@PathVariable("id") Integer id) {
        return userService.deleteUserById(id);
    }

}

6. 体验无缓存 SQL 查询操作

6.1 我们把 UserService 服务类中的 getUserList() 方法上的 @Cacheable 注解先注释掉

6.2 运行我们的 Spring Boot 项目,然后打开我们的浏览器进行访问接口

获取用户列表

http://localhost:8080/api/get

6.3 查看 IDEA 控制台,多次刷新浏览器查询用户列表

我们发现每刷新一次都会执行 SQL 查询语句 JPA 操作查询数据库,恭喜你体验成功!

7. 体验缓存 SQL 查询操作

7.1 我们把 UserService 服务类中的 getUserList() 方法上的 @Cacheable 注解的注释进行还原 

7.2 正式 CRUD 体验缓存逻辑

运行 Spring Boot 项目,浏览器链接到用户列表接口

http://localhost:8080/api/get

现在不管你刷新访问多少次这个接口,都只会从缓存中拿取第一次 SQL 语句查询 MySQL 时的数据,在 IDEA 控制台查看显示的 SQL 查询次数

7.3 体验缓存更新

我们来尝试插入一个用户,访问插入用户路径参数接口

http://localhost:8080/api/insert/newUser/newUser

再次访问用户列表接口

http://localhost:8080/api/get

 因为插入新数据而指定的缓存被删除,然后重新进行查询一次并再一次存入缓存

 其它接口自己操作试试吧~

8. 缓存小总结

  • 对查询进行缓存:注意为 null 时的问题。
  • 注意在进行增删改时:需要及时的更新内存中的缓存,避免缓存 BUG
  • 注意对于集合对象数据:注意缓存的 key 问题,key 是单向的,并不能针对集合中元素的更新,因为没有对集合中单个元素进行 key 的指向。
  • 注意默认 key 问题:未指定 key 默认使用方法中参数值单个或一组进行作为 key 值。
  • 自定义的 String 作为 key 值:需要有单引号引起来 String listKey = "'myKey'" 。

9. 引入 Redis 缓存转变

PS:其实前面已经说过了,从默认缓存,到依赖程序缓存,Spring Boot 只要引入依赖并进行配置文件配置,就能直接从默认缓存转换过来!

注意请提前在电脑装好 Redis 程序,这里就不掩饰了。

9.1 添加 Redis 依赖

在 pom.xml 中添加依赖

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

 刷新 maven 仓库更新依赖

9.2 配置 Redis 密码 (可跳过)

PS设置密码 为了安全一定要设,而且这里如果不绑定 IP 也不设密码的话,Redis 是默认保护模式,只能本地主机访问,不允许其他 IP 访问。 

9.3 配置文件连接 Redis

在全局配置文件 application.properties 中配置

#———————— Redis ——————————
#服务器地址
spring.data.redis.host=127.0.0.1
#服务器端口
spring.data.redis.port=6379
#Redis 服务器连接密码(默认未设置为空), 默认保护模式,只能本地主机访问,有需求需改密码
spring.data.redis.password=
#基于注解的 Redis 统一设置缓存数据有效时间, 单位毫秒(mm)
spring.cache.redis.time-to-live=100000

9.4 Redis 对 Bean 实体类实现序列化接口

PS:Redis 在缓存时,缓存的数据实体类需要进行实现序列化接口,我们的缓存数据对象只有 User ,所以,我们需要对 User 实体类进行实现序列化接口。

修改 com.bean 包下的 User 实体类实现序列化接口

import jakarta.persistence.*;
import lombok.Data;
import org.springframework.stereotype.Component;

import java.io.Serializable;

@Data
@Component
//@Entity
//@Table(name = "t_user")
@Entity(name = "t_user")
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY) //配置主键的生成策略
    @Column(name = "id")
    private Integer id;

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

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

}

9.5 体验 Redis 缓存

PS:我们根据以上步骤配置完全后,就已经完成了从自带缓存 Simple 到 Redis 转换。不过这里我们需要借助 Redis 缓存管理工具进行查看缓存。

通过浏览器访问接口

http://localhost:8080/api/get
http://localhost:8080/api/get/1

Redis Desktop Manager 桌面管理程序查看缓存

命令查看缓存列表 (Windows)

1. 进入 Redis 安装目录,打开 CMD 进入该目录。

2. 执行命令

redis-cli.exe -h <hostname> -p <port>

redis-cli.exe -h 127.0.0.1 -p 6379

3. 执行 keys * 查看全部的键缓存

keys *

六、jar & war 打包及部署

0. 配置 pom.xml 打包规则

0.1 Maven 打包插件

        Spring Boot 项目进行 Jar 和 War 打包需要在 pom.xml 中加入 Mave 打包插件,不过我们在创建项目的时候自动的为我们配置了。

pom.xml 中 Maven 打包插件文件配置

<!-- maven 打包插件 -->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

 0.2 配置打包方式

        默认是 jar 的打包方式,war 打包需要配置,以下是配置,在标签<project>下层配置 :

<!-- (默认 jar) 声明项目打包方式 jar || war -->
<packaging>jar</packaging>

0.3 配置可使用外部 Tomcat 部署

        默认Spring Boot 默认使用内置 Tomcat 引擎,在标签<dependencies>下层配置:

<!-- (默认内置 Tomcat 引擎) 声明使用外部 Tomcat 部署 war -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <!-- 声明两种可外部 Tomcat War 包部署、可内嵌 Tomcat Jar 包部署 -->
    <scope>provided</scope>
</dependency>

0.4 配置打包输出命名 (项目名+版本号)

        在打包时命名输出使用的是 <项目名+定义项目版本号> 命名的,可在标签<project>下层找到进行修改,我这就就默认了:

<groupId>com.example</groupId> <!-- 包名 -->
<artifactId>demo</artifactId> <!-- 定位 -->
<version>0.0.1-SNAPSHOT</version> <!-- 项目版本 -->
<name>demo</name> <!-- 项目名称 -->
<description>demo</description> <!-- 项目描述 -->

0.5 完整 pom.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.3</version> <!-- Spring Boot 版本 -->
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId> <!-- 包名 -->
    <artifactId>demo</artifactId> <!-- 定位 -->
    <version>0.0.1-SNAPSHOT</version> <!-- 项目版本 -->
    <name>demo</name> <!-- 项目名称 -->
    <description>demo</description> <!-- 项目描述 -->
    <!-- (默认 jar) 声明项目打包方式 jar || war -->
    <packaging>jar</packaging>
    <properties>
        <java.version>17</java.version> <!-- Spring Boot 版本对应的 JDK 版本 -->
    </properties>
    <dependencies>
        <!-- (默认内置 Tomcat 引擎) 声明使用外部 Tomcat 部署 war -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <!-- 声明两种可外部 Tomcat War 包部署、可内嵌 Tomcat Jar 包部署 -->
            <scope>provided</scope>
        </dependency>
        <!-- Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- JPA -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- Thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity6</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter-test</artifactId>
            <version>3.0.3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- maven 打包插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!-- Lombok 配置 -->
                    <excludes>
                        <exclude>
                            <groupId>org.project.lombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

pom.xml 配置更改了记得刷新 Maven 仓库配置。

1. 打包跳过测试文件

PS1IDEA 跳过测试类文件打包Maven --> Lifecycle --> test 点击 --> 点击上面【闪电图标】 --> test 按钮被划掉。

当前 Maven 依赖不支持配置跳过测试文件打包,所以我们在 IDEA 指定。

2. Jar 打包部署

2.1 配置 jar 打包方式

        默认是 jar 的打包方式,war 打包需要配置,以下是配置,在标签<project>下层配置 :

<!-- (默认 jar) 声明项目打包方式 jar || war -->
<packaging>jar</packaging>

2.2 打包

PS:IDEA 打包,Maven --> Lifecycle --> package 双击。

        打包完成后,jar 包存放在项目的 target 目录下。

2.3 打包报<编码UTF-8的不可映射字符>解决 (如没有可跳过)

PS:就是某些类文件设置的编码格式不是 UTF-8 问题,如下图更改,不过也能进入配置文件更改。

2.4 命令运行 Jar 包

PS:这里是 Windows 系统,我们用系统命令终端执行运行 jar 包,IDEA 的终端也行。

        打开 CMD --> 系统命令进入你的 jar 包路径 --> 输入如下 java 命令运行 Jar 包

java -jar demo-0.0.1-SNAPSHOT.jar

等待运行完成

PS:我们可以看到内置引擎 Tomcat/10.1.19 版本,以及端口号 8080。

3. War 打包 Tomcat 部署

3.1 配置 war 打包方式

        默认是 jar 的打包方式,war 打包需要配置,以下是配置,在标签<project>下层配置 :

<!-- (默认 jar) 声明项目打包方式 jar || war -->
<packaging>war</packaging>

3.2 主程序入口继承 SpringBootServletInitializer 类

PS:主程序入口继承 SpringBootServletInitializer 来支持外部 Tomcat 部署的 Servlet 支持。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
@EnableCaching //开启 Spring Boot 基于注解的缓存管理支持
@ServletComponentScan //开启基于注解方式 Servlet 组件扫描支持
/*
    继承 SpringBootServletInitializer 类,以提供 Servlet 3.0 支持
    重写 configure(builder) 方法,调用并返回 builder.sources(<SpringBootApplication.class 主程序入口类>); 提供Servlet初始化对象
 */
public class DemoApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(DemoApplication.class); //重写configure()方法为SpringBoot提供Servlet初始化对象
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
        System.out.println("测试中文编码");
    }

}

3.4 打包

PS:IDEA 打包,Maven --> Lifecycle --> package 双击。

        打包完成后,jar 包存放在项目的 target 目录下。

4. Tomcat 部署 war 包 (Windows)

4.1 安装 Tomcat

PS:在部署 war 包前我们需要知道 war 包的需求版本,我们的 Spring Boot 版本以及对应 Java JDK 都是有对应版本的 Tomcat ,其实也就是年代原因,某些 Tomcat 版本不支持高版本的 Spring Boot 。

版本需求

  • Spring Boot 版本:3.2.3
  • Tomcat :10
  • JDK:19

资源在文章开头提供有,安装目录:

4.2 配置 Tomcat 环境变量

PS:Tomcat 在启动服务命令前,我们需要指定 Tomcat 的安装路径、JRE 运行路径、为了快速启动 Tomcat 还可设置 bin 启动脚本命令路径。

Tomcat 10 环境变量配置表
变量名说明
CATALINA_HOMETomcat 安装路径
JRE_HOMEJRE 路径,系统如果只装了 JDK 就是 JDK 路径即可
%CATALINA_HOME%\bin

这一个一个引用变量值,为了快速启动 Tomcat 而配置的,直接使用启动命令。

请配置到 path 变量中。

4.3 运行 Tomcat

PS把打包好的 war 包拷贝到 Tomcat 安装路径的 webapps 目录下,并重命名这个 war 包为你想要的一个链接主名

我这里命名为 app.war

打开终端 CMD 运行 Tomcat 的 bin 目录下的 startup.bat

我这里配置了环境变量 Tomcat 的 bin 路径,所以直接运行

startup.bat

可以看到新增了个窗口运行 Tomcat

浏览器访问 Tomcat :

http://localhost:8080/

4.4 浏览器访问项目

PS:使用 Tomcat 访问的链接是有变化的,我们在第一次运行Tomcat 解包 war 时,就会用 war 包的名字进行命名链接的主名。

可以看到 Tomcat 安装目录的 webapps 下就生成了 app 文件夹,这就是在重命名 war 包的目的

通过浏览器访问,链接需要加自己定义<app>名字进行访问

http://localhost:8080/app/test

4.5 ROOT 根路径去除定义的文件夹名头访问链接

PS:其实就是在 Tomcat 安装路径 webapps 目录下,把自己的项目的文件名改成 ROOT 即可。把原来的 ROOT 文件夹命名成其它的名字(随意)。

我这里因为有个 app.war 包,要是改动 app 文件夹名就又会重新生成一个 app 文件夹名的项目,所以我这里把 app.war 包先删除,再改动

  1. 原来的  ROOT  --->  tomcat
  2. 项目名  app  --->  ROOT

现在关闭并重启我们的 Tomcat 服务

访问我们的项目使用链接 :

http://localhost:8080/test

  • 26
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虚妄狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值