44444

44444

使用Mybatis实现数据库编程---在一个项目中,除了关联表,绝大部分的数据表都至少需要实现以下功能

插入1条数据

批量插入数据

根据id删除某一条数据

根据若干个id批量删除某些数据

根据id修改数据

统计当前表中数据的数量

根据id查询数据详情

查询当前表中的数据列表

一、在配置类中使用@MapperScan指定接口文件所在的根包,在项目的根包下创建config.MybatisConfiguration配置类(添加了

@Configuration注解的类),在此类上配置@MapperScan注解

二、在配置文件中通过mybatis.mapper-locations属性来指定XML文件所在的位置,在application.properties中添加配置

mybatis.mapper-locations=classpath:mapper/*.xml,然后在src/main/resources下创建mapper文件夹

xml version="1.0" encoding="UTF-8"?>

-//mybatis.org//DTD Mapper 3.0//EN" "" target="_blank">http://mybatis.org/dtd/mybatis-3-mapper.dtd"" target="_blank">>

三、创建实体类,在项目根包下创建实体类pojo.entity.Album,在类中声明与数据表对应的各属性。在编写pojo类之前,先在项目

中添加依赖

org.projectlombok

lombok

1.18.20

provided

在任何pojo类上,可以添加@Data注解,则Lombok框架会自动在编译期生成getter、setter、hashcode、equals方法

另外任何pojo类,都应该实现Serializable接口。

四、创建接口,并声明抽象方法。在项目的根包下创建mapper.AlbumMapper接口,并在接口中添加"插入1条数据"的抽象方法

public interface AlbumMapper{

int insert(Album album);

}

五、添加XML文件,并在此文件中配置抽象方法映射的SQL语句。在src/main/resources/mapper下,

六、创建测试类,编写并执行测试方法,在src/test/java下的根包下,创建mapper.AlbumMapperTests类,在类上添加

@SpringBootTest注解,并在类中编写测试方法

MySQL数据类型与Java类中的数据类型的对应关系-----

tinyint/smallint/intInteger

bigintLong

char/varchar/text系列String

date_timeLocalDateTime

关于抽象方法的声明--

返回值类型:如果执行的SQL语句是增、删、改类型的,返回值类型始终使用int,表示"受影响的行数",不建议使用void

方法的名称:参考阿里编码规范,且不推荐重载

参数的列表:根据需要执行的SQL语句中的参数来设计,如果参数较多且具有相关性,则推荐封装

密码加密----用户注册时(管理员添加新的账号信息时)的密码必须经过某种算法进行编码,并将得到的结果存储到数据库,而不允许

将原始密码直接存储到数据库中。原始密码通常可以称之为密码的原文,或称之为明文密码,经过编码处理的结果可

称之为密文。

如果直接存储明文密码,容易导致用户账号被窃取的安全问题,基于当下主流的网络技术,这类安全问题通常是企业

内部管理问题导致的!简单来说,对密码进行加密处理,主要防的就是内部员工(例如能够直接接触到数据库服务器的

员工,无论是进入机房或是远程登录)

对明文密码进行编码处理时,不可以使用任何加密算法!因为所有加密算法都是可以逆向运算的,即根据算法类型、加密

参数、密文,可以通过运算得到原文。通常,加密算法只是用于保障数据在传输过程中的安全性!

对于需要存储下来的密码,且只需要验证原密码是否匹配,不需要(不允许)通过逆向运算根据密文得到原文时,应该使

用消息摘要算法对密码原文进行编码处理

消息摘要算法的典型特征:

消息相同时,摘要必然相同

使用固定的某种消息摘要算法,无论消息的长度多少,摘要的长度是固定的

消息不同时,摘要极大概率不会相同,理论上必然存在N个不同的消息,通过编码得到的摘要是完全相同的

由于摘要的种类也非常多,设计得非常好的算法会使得这种概率非常低

所有消息摘要算法都是不可以逆向运算的

消息摘要算法的结果通常会使用十六进制数来表示,通常,运算结果相对简单的消息摘要算法的结果长度也有32位

十六进制数组成,还原成二进制数就是128个二进制数组成,这样的算法称之"128位算法",同理,如果某个

结果是由64个十六进制数组成,还原成二进制数就是256个二进制数,则称之为"256位算法"

在Spring Boot项目中,已有DigestUtils工具类中的md5DigestAsHex()方法提供了MD5算法进行编码

Lombok框架--@Data:添加在类上,可以在编译期生成getter、setter、hashcode、toString、equals方法等,使用此注解,必须保证

当前类的父类存在无参构造方法

@Setter:可以加在属性上,也可以添加在类上

@Getter:同@Setter

@EqualsAndHashCode:添加在类上,生成规范的equals和hashcode方法

@ToString:添加在类上

@Slf4j:添加在类上添加,日志注解,可以添加在测试类上

Slf4j日志---在Spring Boot项目中,基础依赖项(spring-boot-starter)中已经包含了日志的相关依赖项

在添加了Lombok依赖项后,可以在类上添加@Slf4j注解,则Lombok框架会在编译期生成名为log的变量,可调用此变量

的方法来输出日志

显示级别:根据日志内容的重要程度,从不重要到重要依次为————

trace:跟踪信息,可能包含不一定关注,但是包含了程序执行流程的信息

debug:调试信息,可能包含一些敏感内容,比如关键的数据的值

info :一般信息

warn :警告信息

error:错误信息

在Spring Boot项目中,默认的日志显示级别为【info】,将只会显示application.properties中配置

logging.level.根包=日志显示级别 来设置当前显示级别

在开发实践中,应该使用trace或debug级别的日志来输出与流程相关的、涉及敏感数据的日志,使用info输出一般的、

被显示在控制台也无所谓的信息,使用warn和error输出更加重要的需要关注的日志

输出日志时,通常建议使用void trace(String message,Object... args)方法(也有其他级别日志的同样参数列表的方法)

在第1个参数String message中可以使用{}作为占位符,表示某变量的值。后面可以跟N个值,类似于Vue的插值

Profile配置-同一个项目,在不同的环境中,需要的配置值可能是不同的,例如日志的显示级别、连接数据库的配置参数等,由Spring

框架提供,在Spring Boot中更是简化了此项操作,它允许使用application-自定义名称.properties作为Profile配置文件

的文件名

这类配置文件默认不会加载,在applicaton.properties中通过spring.profiles.active=自定义名称 来激活配置文件

当主从配置文件配置冲突时,以从文件配置为准(范围越小越优先)

YAML配置----是一种编写配置文件的语法,表现为使用.yml作为扩展名的配置文件,Spring框架默认不支持此类配置文件,而Spring

Boot的基础依赖项中已经包含解析此类文件的依赖项,所以在Spring Boot项目可以直接使用此类配置文件

在Spring Boot项目中,使用.properties和.yml配置是等效的

插入数据时获取自动编号的id--

如果表中的id是自动编号的,在标签上,可以配置useGeneratedKeys="true"和keyProperty="id",可以自动获取自动编号

的id值,并由Mybatis自动赋值到参数对象的对应的属性id上

批量插入多条数据----代码如下

INSERT INTO pms_album(

name,description,sort

)VALUES

(#{album.name},#{album.description},#{album.sort})

根据id更新一条数据--代码如下,Mybatis的标签会自动去掉if判断完后最后一个值的逗号

UPDATE pms_album

name=#{name},

description=#{description},

sort=#{sort},

WHERE id=#{album.id}

查询----每个

SELECT

FROM pms_category

WHERE id=#{id}

id,name,parent_id,depth,keywords,sort,icon,enable,is_parent,is_display

关于Service-----在项目中,应该使用Service组件来处理业务相关的代码,以此来设计业务流程、业务逻辑,以保证数据的完整性、

有效性。

通常,Service应该由接口和实现类来组成,实现类上要加@Service注解

Service抽象方法的声明原则————

返回值类型:仅以操作成功为前提来设计,操作失败用通过抛出异常来表示,异常应继承自RuntimeException

方法名称 :自定义的、规范的,无其它约束

参数列表 :根据客户端提交的请求参数来设计,如果参数较多,且具有相关性,则应该封装

添加相册的业务--先制定业务规则:相册名称必须是唯一的

处理添加相册请求----开发控制器相关代码时,需要项目中添加spring-boot-starter-web依赖项,此依赖项包含了

spring-boot-starter依赖项

关于处理异常----在服务器端项目中,如果某个抛出的异常始终没有被处理,则默认会向客户端响应500状态代码

所以在服务器端项目中,必须对异常进行处理,因为如果不处理,软件的使用者可能不清楚出现异常的原因,也不

知道如何调整请求参数来解决此问题,甚至可能反复尝试提交错误的请求(刷新页面),对于服务器端而言,也是浪费

了一些性能

Spring MVC框架统一处理异常的机制----

每种类型的异常只需要编写1段相关的处理代码即可,当使用这种机制时,在各处理请求的方法中,将不再使用try-catch语句

块来包裹可能抛出异常的方法并处理,则控制器类中处理请求的方法都将是抛出异常的(虽然不必在代码中显示的throws),

这些异常会被Spring MVC框架捕获并尝试调用统一处理异常的方法如下:

注解 :必须添加@ExceptionHandler注解

访问权限 :public

返回值类型:参考处理请求的方法

方法名称 :自定义

参数列表 :必须有1个异常类型的参数,表示Spring MVC框架调用处理请求的方法时捕获的异常,并且,可以按需添加

HttpServletRequest、HttpServletResponse等少量特定类型的参数,不可以随意添加其他参数

一般会将统一处理异常的代码放在专门的类中,并在此类上添加@RestControllerAdvice注解,此类中特定的方法将作用于整个

项目任何处理请求的过程

限制请求方式----以获取数据为主要目的的用get,其他用post

在RequestMapping(value="/reg",method=RequestMethod.POST)设置该方法只能允许post请求

Spring MVC框架已经定义了限制请求方式的注解,只能添加在方法上:

@GetMapping :把请求方式限制为get

@PostMapping:把请求方式限制为post

@PutMapping:把请求方式限制为put

@DeleteMapping:把请求方式限制为delete

@PatchMapping :把请求方式限制为patch

关于RESTful-----是一种软件的设计风格(不是规定,也不是规范)

典型表现:一定是响应正文的,服务器端处理完请求后将响应数据,不会由服务器响应页面到客户端

通常会将具有唯一性的请求参数设计到url中,成为url的一部分

严格区分4种请求方式,在许多业务系统其实并不这样设计,增加数据使用post、删除数据

使用delete、修改数据使用put、查询数据使用get

Spring MVC框架很好的支持了RESTful风格的设计,当需要在url中使用变量值时,可以使用{自定义名称}

作为占位符,同时如果想拿到传过来的参数,可以在参数列表添加@PathVariable注解,并声明对应

的变量来接收,通常会将占位符中的自定义名称和方法的参数名称保持一致,如果因为某些原因

无法保持一致,则需要配置@PathVariable("参数"),此注解参数与占位符一致即可

在开发实践中,可以将处理请求的方法的参数类型设计为期望的类型,例如将id设计为Long类型的

但是,如果这样设计,必须保证请求中的参数值是可以被正确转换为Long类型的,否则会出现400错误

为了尽量保证匹配的准确性、保证参数值可以正常转换,在设计占位符时,可以在占位符名称右侧添加冒号

并在冒号右侧使用正则表达式来限制占位符的值的格式

// http://localhost:8080/album/9527/delete

@RequestMapping("/{id:[0-9]+}/delete")

public String delete(@PathVariable Long id){

String message = "尝试删除id值为"+id+"的相册";

log.debug(message);

return message;

}

一旦使用正则表达式后,多个不冲突的占位符的设计是允许共存在的,例如:

// http://localhost:8080/album/hello/delete

@RequestMapping("/{id:[a-z]+}/delete")

public String delete(@PathVariable String name){

String message = "尝试删除id值为"+id+"的相册";

log.debug(message);

return message;

}

另外,没有使用占位符的设计,与使用了占位符的设计,也是允许共存的,例如:

// http://localhost:8080/album/test/delete//精准匹配优先级是最高的

@RequestMapping("/test/delete")

public String delete(@PathVariable Long id){

String message = "尝试删除id值为"+id+"的相册";

log.debug(message);

return message;

}

最后,关于RESTful风格的url设计,如果没有明确的要求,或没有更好的选择,可以设计为:

获取数据列表 :/albums

根据id获取数据 :/albums/1

根据id对数据执行某种操作:/albums/1/delete

关于Knife4j框架-----是一款基于Swagger 2的在线API文档框架,使用Knife4j框架需要:

添加依赖,注意:本次使用的Knife4j必须基于Spring Boot的版本在2.6之前(2.6及更高的版本不可用)

com.github.xiaoymin

knife4j-spring-boot-starter

2.0.9

需要在主配置文件中添加配置

knife4j.enable=true

添加配置类

package cn.tedu.jsd2207csmall.product.config;

import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import springfox.documentation.builders.ApiInfoBuilder;

import springfox.documentation.builders.PathSelectors;

import springfox.documentation.builders.RequestHandlerSelectors;

import springfox.documentation.service.ApiInfo;

import springfox.documentation.service.Contact;

import springfox.documentation.spi.DocumentationType;

import springfox.documentation.spring.web.plugins.Docket;

import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

/**

* Knife4j配置类

*

* @author [email protected]

* @version 0.0.1

*/

@Slf4j

@Configuration

@EnableSwagger2WebMvc

public class Knife4jConfiguration {

/**

* 【重要】指定Controller包路径

*/

private String basePackage = "cn.tedu.jsd2207csmall.product.controller";

/**

* 分组名称

*/

private String groupName = "product";

/**

* 主机名

*/

private String host = "http://java.tedu.cn";

/**

* 标题

*/

private String title = "酷鲨商城在线API文档--商品管理";

/**

* 简介

*/

private String description = "酷鲨商城在线API文档--商品管理";

/**

* 服务条款URL

*/

private String termsOfServiceUrl = "http://www.apache.org/licenses/LICENSE-2.0";

/**

* 联系人

*/

private String contactName = "Java教学研发部";

/**

* 联系网址

*/

private String contactUrl = "http://java.tedu.cn";

/**

* 联系邮箱

*/

private String contactEmail = "[email protected]";

/**

* 版本号

*/

private String version = "1.0.0";

@Autowired

private OpenApiExtensionResolver openApiExtensionResolver;

public Knife4jConfiguration() {

log.debug("创建配置类对象:Knife4jConfiguration");

}

@Bean

public Docket docket() {

String groupName = "1.0.0";

Docket docket = new Docket(DocumentationType.SWAGGER_2)

.host(host)

.apiInfo(apiInfo())

.groupName(groupName)

.select()

.apis(RequestHandlerSelectors.basePackage(basePackage))

.paths(PathSelectors.any())

.build()

.extensions(openApiExtensionResolver.buildExtensions(groupName));

return docket;

}

private ApiInfo apiInfo() {

return new ApiInfoBuilder()

.title(title)

.description(description)

.termsOfServiceUrl(termsOfServiceUrl)

.contact(new Contact(contactName, contactUrl, contactEmail))

.version(version)

.build();

}

}

完成后,重新启动项目,可以通过 http://localhost:8080/doc.html 查看在线API文档并可使用其中的调用功能等

在使用Knife4j时,应该使用相关注解,将API文档配置得更加易于阅读和使用:

@Api:添加在控制器类上,通过此注解的tags属性,可以指定模块名称,并且可以在模块名称前自行

添加数字序号,以实现排序效果,框架会根据各控制器类的@Api注解的tags属性值进行升序

@ApiOperation:添加在处理请求的方法上,通过此注解的value属性,可以指定业务名称

@ApiOperationSupport:添加在处理请求的方法上,通过此注解的order属性(数值型),可以指定

排序序号,框架会根据此属性值升序排列

通常指定order属性为三位数,且1开头代表insert操作

2开头代表delete操作

3开头代表update操作

4开头代表select操作

9开头代表废弃的操作

@ApiModelProperty:添加在DTO、VO类的属性上,配置属性的名称和是否可以为空,也可以加在响应结果上

@ApiModelProperty(value="相册名称",required=true)

@ApiImplicitParam:添加在处理请求的方法上,配置不是pojo类型的参数

@ApiImplicitParam(name="id",value="相册id",required=true,dataType="long")

@ApiImplicitParams:添加在处理请求的方法上,配置多个不是pojo类型的参数

@ApiImplicitParams({数组里面放多个@ApiImplicitParam注解})

安装Node.js-----搭建前端项目的仓库管理器,下载Node.js后并安装

npm -v查看是否安装成功

npm config set registry https://registry.npmmirror.com配置npm源

npm config get registry查看npm源

安装VUE Cli-----本项目的前端项目将使用"Vue脚手架"项目来开发,必须先在本机安装Node.js,然后在cmd中输入

npm install -g @vue/cli安装vue脚手架

vue -V检查vue脚手架是否安装完成

如果卡住了,Ctrl+C强行终止,并再次尝试安装

创建Vue脚手架项目---在创建项目之前,先确定当前操作位置,例如:C:\Users\tarena,此操作位置决定项目创建位置

通过vue create +项目名称即可创建项目,例如:vue create jsd-csmall-web-client,按回车等待

接下来选择项目的参数① Manually select features

② Babel、Router、Vuex

③ 2.x

④ In package.json

然后把创建好的项目剪切到常用的文件夹,然后在idea中打开,在终端面板中npm run serve启动项目,然后

根据提示的url在浏览器中打开可以看到默认的页面,前端项目没有终止按钮,只能在终端面板Ctrl+C

配置前后端端口--避免每次启动项目端口不一样发生冲突,在后端的从配置文件中配置后端端口server.port=9080

在前端项目的package.json中在serve值的后面手动指定端口为 -- 9000

关于视图组件----以.vue为拓展名的文件称之为"视图组件",将负责页面的显示,包括编写样式、JavaScript代码,一定程度上,与

传统项目中的.html文件的作用是相似的,主要由3部分组成:

:用于设计页面元素,即页面需要显示什么,此标签有且只有一个直接子标签

:用于设计样式规则,即CSS

:用于编写JavaScript程序

默认的页面效果--与运行效果相关的常用文件有:

src/App.vue:是项目中唯一的.html文件默认绑定的视图组件,可理解为项目的入口视图组件,并且,此页面

中设计的内容是始终显示的,在此视图中,有标签,此标签表示"将由其他

视图组件来显示"

src/router/index.js:是默认的路由配置文件,此文件中的routes常量配置了路径与视图组件的对应关系,决定了

标签将由哪个视图组件来显示

关于routes的配置:此属性是数组类型的,其中的各元素通常称之为一个个的"路由对象",每个路由对象中主要

配置path和component属性,即配置路径与视图组件的对应关系,关于component,如果某个

视图是类似"主页"定位,推荐通过import语句导入,其它视图推荐使用import()函数导入

Vue脚手架项目的结构-----[.idea] :是通过idea编辑项目时,由idea创建的文件夹,不需要人为干预

[node_modules]:当前项目的依赖项文件夹,不应该人为干预,在使用Git等管理工具时,通常并不会将

此文件夹提交到Git服务器,从Git服务器下载得到的项目也不会包含此文件夹及内容,

如果没有这个文件夹,则项目无法编译、运行。可以通过执行npm install命令安装

项目所需的依赖项

[public] :静态资源文件夹,此文件夹下的内容是可以直接被访问的,不会经过项目的编译过程,

通常会在此文件下存放css、js、图片文件等

favicon.ico:当前网站的图标文件,此文件是固定文件名的

index.html :当前项目中仅有的唯一.html文件

[src] :项目的源代码文件夹

[assets]:静态资源文件夹,与public文件夹不同,此处的静态资源应该是不随程序

运行而变化的

[components]:被其它视图导入、调用的视图组件的文件夹

[views] :视图组件文件夹

[router]:路由配置文件的文件夹

index.js:默认的路由配置文件

[store] :存储全局的量的文件所在的文件夹

index.js:默认的存储全局的量的文件

App.vue :直接绑定到了index.html的视图组件

main.js :项目的主配置文件

.gitignore :用于配置"提交Git时忽略哪些文件、文件夹"

babel.config.js:暂不关心

jsconfig.json :暂不关心

LICENSE :作为Git的开源项目应该包含"许可协议",此文件就是"许可协议"文件

package.jsion :当前项目的配置文件,类似Maven项目中的pom.xml文件,不建议手动修改

package-lock.json:锁定的当前项目配置文件,此文件不允许手动编辑,即使编辑了,也会自动还原

安装并配置ELEMENT UI----在终端窗口中执行npm i element-ui -S

需要在main.js中添加配置:

import ElementUI from 'element-ui';

import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

嵌套路由----在设计前端的视图组件时,如果根级视图(通常是App.vue)使用了则表示相关区域将由另一个视图组件

来完成显示,而另一个视图组件中也使用了就会出现的嵌套,则需要在router/index.js

中配置嵌套路由。可以给路由对象配置children属性,此属性配置方式与routes完全一样

安装axios---npm i axios -S

然后在main.js中添加配置:

import axios from 'axios';

Vue.prototype.axios = axios;

跨域访问----客户端与服务端不在同一台主机上,在默认情况下,不允许发送跨域访问请求

在Spring Boot项目中,需要使用配置类实现WebMvcConfigurer接口,并重写addCorsMappings()方法进行配置

public void addCorsMappings(CorsRegistry registry) {

registry.addMapping("/**")

.allowedOriginPatterns("*")

.allowedHeaders("*")

.allowedMethods("*")

.allowCredentials(true)

.maxAge(3600);

}

@RequestBody----在Spring MVC项目中(包括添加了spring-boot-starter-web依赖项的Spring Boot项目),可以在处理请求的方法

的参数列表中,在某参数上添加@RequestBody注解。如果请求参数添加了@RequestBody注解,则客户端提供的

请求参数必须是对象格式。

如果请求参数没添加@RequestBody注解,则客户端提供的请求参数必须是FormData格式

使用qs------是一款可以将对象格式的数据转换为FormData格式的框架,npm i qs -S

然后在main.js中添加配置:

import qs from 'qs';

Vue.prototype.qs=qs;

let formData = this.qs.stringify(this.ruleForm)

响应JSON格式字符串--在Spring Boot项目中,只需要自定义类型,在此类型中设计响应结果中包含的数据属性,并在处理请求、处理

异常的方法上使用此类型作为返回值类型即可,在类上加@Data注解

检查请求参数----所有请求参数都应该对数据的基本格式进行检查,此类检查可以在客户端直接进行,但是,对于服务器端而言,客户

端的检查结果应该视为"不信任的",因为可能某些请求是绕过了客户端的检查的,客户端软件可能存在被篡改,

客户端软件可能不是最新版本,与服务器期望的并不一致,以及其它原因

所以服务器端必须在接收到请求参数的第一时间,就对请求参数的基本格式进行检查

使用Validation--来检查请求参数的基本格式,先添加依赖项

org.springframework.boot

spring-boot-starter-validation

在控制器中,对于封装类型的请求参数,应该先在请求参数之前添加@Valid或@Validated注解,表示将需要对此请求

参数的格式进行检查,然后在此封装的类型中,在需要检查的属性上,添加检查注解,例如:

@NotNull(message = "必须提交相册名称")非空检测,并自定义错误提示

当请求参数可能出现多种错误时,也可以选择"快速失败"的机制,它会使得框架只要发现错误,就停止检查其他规则

这需要在配置类ValidationConfiguration中进行配置如下:

//Builder模式

//链式语法

@Bean

public Validator validator(){

return Validation.byProvider(HibernateValidator.class)

.configure()

.failFast(true)

.buildValidatorFactory()

.getValidator();

}

当处理请求的方法的参数时未封装的(例如Long id),检查时,需要:

在当前控制器类上添加@Validated注解

在需要检查的请求参数上添加检查注解@Range(min = 1,message = "删除相册失败,id无效")

关于检查注解----@NotNull :非空检测

@Range :整数数值范围检测

@NotEmpty:不允许长度为0的字符串

@NotBlank:不允许全部为空白字符

@Pattern :此注解有regexp属性,配置正则表达式

除了@NotNull注解以外,其他注解均不检查请求参数为null的情况

VUE生命周期方法-----created(){}刚刚创建出来

mounted(){}挂载,准备就绪,推荐使用

基于Spring JDBC的事务管理---事务(Transaction),是关系型数据库中一种能够保障多个写操作(增、删、改)要么全部成功,要么

全部失败的机制

在编程式事务管理过程中,需要先开启事务(BEGIN),然后执行数据操作,当全部完成,需要提交

事务(COMMIT),如果失败,则回滚事务(ROLLBACK)

在基于Spring JDBC的项目中,只需要使用声明式事务即可,只需要在业务方法上添加

@Transactional注解,即可使得方法有事务性

Spring JDBC实现事务管理大致是:

开启事务:Begin

try{

执行事务方法:即数据访问操作

提交事务:Commit

}catch(RuntimeException e){

回滚事务:Rollback

}

所以,Spring JDBC在事务管理中,默认将基于RuntimeException进行回滚,可以通过@Transactional的

rollbackFor或rollbackForClassName属性来修改,例如:

@Transactional(rollback = {NullPointerException.class,IndexOutOfBoundsException.class})

@Transactional(rollbackForClassName = {"java.lang.NullPointerException",...})

还可以通过noRollbackFor或noRollbackForClassName属性来配置对于哪些异常不回滚

其实,@Transactional注解还可以添加在:

实现类上 :作用于当前类中所有业务方法

实现类业务方法:作用于添加了注解的业务方法

接口上 :作用于实现了此接口的类中的所有业务方法

接口业务方法 :作用于实现了此接口的类中的重写的当前业务方法

如果都添加了,且配置了相同的参数,则范围越小越优先,推荐添加在接口或接口中的业务方法上

添加在测试方法上可以自动回滚

关于获取受影响行数--对于写操作,一般都要在业务方法获取受影响的行数,并判断是否与预期一致,如果不一致就抛出异常

int rows = categoryMapper.insert(category);

if(rows != 1){

String message = "添加类别失败,服务器繁忙,请稍后再尝试";

log.debug(message);

throw new ServiceException(ServiceCode.ERR_INSERT,message);

}

关于Spring Security框架-----主要解决了认证与授权相关的问题,先添加Spring Boot Security依赖项

org.springframework.boot

spring-boot-starter-security

关于Spring Security配置类---在Spring Boot项目中,创建config.SecurityConfiguration配置类,需要继承自

WebSecurityConfigurerAdapter,并重写configure(HttpSecurity http)方法进行配置

protected void configure(HttpSecurity http) throws Exception {

// 白名单

String[] urls = {

"/doc.html",

"/**/*.js",

"/**/*.css",

"/swagger-resources",

"/v2/api-docs"

};

// 将防止伪造跨域攻击的机制给禁用

http.csrf().disable();

// super.configure(http);

http.authorizeRequests() //管理请求授权

.mvcMatchers(urls)

.permitAll() //直接许可

.anyRequest() //除了以上配置过的其它所有请求

.authenticated(); //要求是"已通过认证的"

http.formLogin(); //启用登录表单

}

关于伪造的跨域攻击--即CSRF主要是基于服务器端对浏览器的信任,在多选项卡的浏览器中,如果在X选项卡中登录,在Y选项卡中

的访问也会被视为"已登录"

在Spring Security框架中默认开启了防止伪造的跨域攻击的机制,其基本做法就是需要在POST请求中要求

客户端提交其随机生成的一个UUID值

关于登录账号----默认情况下,Spring Security框架提供了默认的用户名user和启动时随机生成的UUID密码,如果需要自定义登录

账号,可以自定义类,实现UserDetailsService接口,重写接口中的如下方法:

UserDetails loadUserByUsername(String username);

Spring Security框架在处理认证时,会自动根据提交的用户名(用户在登录表单中输入的用户名)来调用以上方法

以上方法应该返回匹配的用户详情(UserDetails类型的对象),接下来,Spring Security会自动根据用户详情

(UserDetails对象)来完成认证过程,例如判断密码是否正确

在根包下创建security.UserDetailsServiceImpl类,在类上添加@Service注解,实现接口并重写方法:

public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

if("root".equals(s)){

UserDetails userDetails = User.builder()

.username("root")

.password("1234")

.disabled(false)

.accountLocked(false)

.accountExpired(false)

.credentialsExpired(false)

.authorities("暂时给出的假权限标识")

.build();

log.debug("{}",userDetails);

return userDetails;

}

return null;

}

在Spring Security配置类中添加密码编码器(也可以添加BCrypt编码器,对应上面也要修改):

@Bean

public PasswordEncoder passwordEncoder(){

return NoOpPasswordEncoder.getInstance();

}

使用前后端分离的登录模式----使用Service处理登录认证,调用AuthenticationManager的authenticate()方法处理认证,可以通过重写

配置类中的authenticationManagerBean()方法,并添加@Bean注解来得到,如下:

@Bean

@Override

public AuthenticationManager authenticationManagerBean() throws Exception {

return super.authenticationManagerBean();

}

@Autowired

private AuthenticationManager authenticationManager;

@Override

public void login(AdminLoginDTO adminLoginDTO) {

log.debug("开始处理【管理员登录】的业务,参数:{}", adminLoginDTO);

Authentication authentication

= new UsernamePasswordAuthenticationToken(

adminLoginDTO.getUsername(), adminLoginDTO.getPassword());

authenticationManager.authenticate(authentication);

简历技术描述参考----【了解/掌握/熟练掌握】开发工具的使用,包括:Eclipse、IntelliJ IDEA、Git、Maven

【了解/掌握/熟练掌握】Java语法

【理解/深刻理解】面向对象编程思想

【了解/掌握/熟练掌握】Java SE API,包括:String、日期、IO、反射、线程、网络编程、集合、异常等

【了解/掌握/熟练掌握】HTML、CSS、JavaScript前端技术

【了解/掌握/熟练掌握】前端相关框架技术及常用工具组件,包括:jQuery、Bootstrap

Vue脚手架、Element UI、axios、qs、富文本编辑器等

【了解/掌握/熟练掌握】MySQL的应用

【了解/掌握/熟练掌握】DDL、DML的规范使用

【了解/掌握/熟练掌握】数据库编程技术,包括:JDBC、数据库连接池(commons-dbcp、commons-dbcp2、Hikari、druid)

及相关框架技术,例如:Mybatis Plus等

【了解/掌握/熟练掌握】主流框架技术的规范使用,例如:SSM(Spring,Spring MVC, Mybatis)

Spring Boot、Spring Validation、Spring Security等

【理解/深刻理解】Java开发规范(参考阿里巴巴的Java开发手册)

【了解/掌握/熟练掌握】基于RESTful的Web应用程序开发

【了解/掌握/熟练掌握】基于Spring Security与JWT实现单点登录

关于登录的判断标准--在Spring Security框架中,对于登录的判断标准是:在SecurityContext中是否存在Authentication对象,如果

存在,Spring Security框架会根据Authentication对象识别用户的身份、权限等,如果不存在,视为未登录

在默认情况下,Spring Security框架也是基于Session来处理用户信息的

关于Session-----为了能够识别客户端身份,当某客户端第1次向服务器端发起请求时,服务器端将向客户端响应一个JSESSIONID数据

(其本质是一个UUID数据),在客户端后续的访问中会自动携带此JSESSIONID,以至于服务器端能够识别客户端的身份

同时在服务器端还有一个Map结构的数据,此数据使用JSESSIONID作为key,所以每个客户端在服务器端都有一个与之

对应的在此Map中的value,也就是Session数据

缺点:不适合长时间保存数据,因为Session是内存中的数据,并且所有来访的客户端在服务器端都有对应的Session

数据,就必须存在Session清除机制,因为如果长期不清除,随着来访的客户端越来越多,将占用越来越多的

内存,通常会将Session设置为15分钟或最多30分钟清除

默认不适用于集群或分布式系统,因为Session是内存中的数据,所以默认情况下,Session只存在于与客户端

交互的那台服务器上,如果使用了集群,客户端每次请求的服务器都不是同一台服务器,则无法有效的识别

客户端的身份。【可以通过共享Session机制解决】

Token---票据、令牌

由于客户端种类越来越多,目前主流的识别用户身份的做法都是使用Token机制,Token可以理解为"票据",例如现实生活中

的火车票,某客户端第1次请求服务器,或执行登录请求,即可视为购买火车票行为,当客户端成功登录,相当于成功购买

了火车票,客户端的后续访问应该携带Token,相当于乘坐火车需要携带购票凭证,则服务器端可以识别客户端的身份,相当

于火车站及工作人员可以识别携带了购买凭证的乘车人

与Session最大的区别在于:Token是包含可识别的有效信息的,长时间保存用户信息

JWT-----JSON WEB TOKEN,是一种使用Json格式来组织数据的Token,不能放隐私数据,先添加依赖项

io.jsonwebtoken

jjwt

0.9.1

void generate() {

Date date = new Date(System.currentTimeMillis() + 2 * 60 * 1000);

Map claims = new HashMap<>();

claims.put("id", 9527);

claims.put("username", "liucangsong");

String jwt = Jwts.builder()

.setHeaderParam("alg", "HS256")

.setHeaderParam("typ", "JWT")

.setClaims(claims)

.setExpiration(date)

.signWith(SignatureAlgorithm.HS256, secretKey)

.compact();

System.out.println(jwt);

}

void parse() {

String jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6OTUyNywiZXhwIjoxNjY3ODc4ODEzLCJ1c2VybmFtZSI6ImxpdWNhbmdzb25nIn0.c_ZkBOHr2NAUSt2z0918jrNV-lbzPabFu1ClRviWuC0";

Claims claims = Jwts.parser()

.setSigningKey(secretKey)

.parseClaimsJws(jwt)

.getBody();

Object id = claims.get("id", Long.class);

Object username = claims.get("username", String.class);

System.out.println(id + "," + username);

}

登录成功时返回JWT---在处理登录时,当用户登录成功,应该向客户端返回JWT数据,以至于客户端下次提交请求时,可以携带JWT

来访问服务器端,需要自定义过滤器来接收客户端携带的JWT数据

@Component

public class JwtAuthorizationFilter extends OncePerRequestFilter {

public static final int JWT_MIN_LENGTH = 113;

public JwtAuthorizationFilter() {

log.debug("创建过滤器对象JwtAuthorizationFilter");

}

@Override

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

log.debug("开始执行过滤");

String jwt = request.getHeader("Authorization");

log.debug("获取客户端携带的JWT:{}", jwt);

// 检查是否获取到了基本有效的JWT

if (!StringUtils.hasText(jwt) || jwt.length() < JWT_MIN_LENGTH) {

// 对于无效的JWT,直接放行,交由后续的组件进行处理

log.debug("获取到的JWT被视为无效,当前过滤器将放行······");

filterChain.doFilter(request, response);

return;

}

// 尝试解析JWT

log.debug("获取到的JWT被视为有效,准备解析······");

Claims claims = Jwts.parser()

.setSigningKey("abcdefg")

.parseClaimsJws(jwt)

.getBody();

String username = claims.get("username", String.class);

// 处理权限信息

List authorities = new ArrayList<>();

GrantedAuthority authority = new SimpleGrantedAuthority("这是一个假权限");

authorities.add(authority);

// 创建Authentication对象

Authentication authentication = new UsernamePasswordAuthenticationToken(username,null,authorities);

// 将Authentication对象存入到SecurityContext

SecurityContextHolder.getContext().setAuthentication(authentication);

// 过滤器链继续向后传递,即放行

filterChain.doFilter(request, response);

}

}

为了保证此过滤器能够在Spring Security过滤器之前执行,还应该在SecurityConfiguration中,先自动装配

此过滤器对象,然后将过滤器对象添加到Spring Security过滤器之前

关于复杂请求的PreFlight-----PreFlight(预检),当客户端提交的请求自定义了请求头,且请求头中的属性不是常规属性时,就会

触发预检机制,这类请求会被视为复杂请求,浏览器会自动向对应的url提交一个options类型的请求

如果此请求被正常响应,才可以正常提交原本的请求,否则视为预检失败,提示跨域错误

http.cors()启用cors过滤器,此过滤器可以对复杂请求的预检放行

使用配置文件自定义JWT参数---在配置文件中添加配置后,当加载时,会将这些配置读取到Spring框架内置的Environment对象中,

另外,操作系统的配置和JVM配置也会自动读取到Environment中,且配置文件中的配置的优先级是

最低的(会被覆盖),使用@Value注解读取值,其实是从Environment中读取的,并不是直接从配置

文件中读取的,所以添加自定义配置时,命名值得斟酌以免发生冲突

# JWT相关配置

csmall.jwt.secret-key=abcdefg

csmall.jwt.duration-in-minute=14400

@Value("${csmall.jwt.secret-key}")

private String secretKey;

@Value("${csmall.jwt.duration-in-minute}")

private long durationInMinute;

将查询到的多个结果封装到一个集合/数组属性中-----

单点登录----SSO(Single Sign On),表现为客户端只需要在某1个服务器上通过认证,其它服务器也可以识别此客户端的身份

实现手段主要有2种:

使用Session机制,并共享Session,添加依赖项

spring-boot-starter-data-redis结合spring-session-data-redis

使用Token机制

各服务器需要有同样的解析JWT的代码

Spring框架--作用:主要解决了创建对象、管理对象的问题

当项目中需要使用Spring框架时,需要添加的依赖项是:spring-context

创建对象的做法:

在任何配置类(添加了@Configuration)中,自定义方法,返回某种类型的(你需要的)对象,并在方法上添加@Bean注解

此方式创建出来的对象,在Spring容器中的名称就是方法名称

此方法应该是public

此方法的返回值类型,是你期望Spring框架管理的数据的类型

此方法的参数列表,建议为空

此方法的方法体,应该是自行设计的,没有要求

配置组件扫描,并在组件类上添加组件注解

此方式创建出来的对象,在Spring容器中的名称默认是将类名首字母改为小写

例如:类名是AdminController,则对象在Spring容器中的名称为adminController

此规则仅适用于类名的第1个字母大写,且第2个字母小写的情况,如果不符合此规则

则对象在Spring容器中的名称就是类名

可以通过组件注解的参数来指定名称

在任何配置类上,添加@ComponentScan,当加载此配置类时,就会激活组件扫描

可以配置@ComponentScan的参数,此参数应该是需要被扫描的根包(会扫描所配置的包,及其所有子孙包)

且此注解参数的值是数组类型的

例如:

@ComponentScan("cn.tedu")

如果没有配置@ComponentScan的参数中的根包,则组件扫描的范围就是当前类的包及其子孙包

需要在各组件类上添加组件注解,才会被创建对象,常见的组件注解有:

@Component:通用注解

@Controller:控制器类的注解

@RestController:仅添加Spring MVC框架后可使用

@ControllerAdvice:仅添加Spring MVC框架后可使用

@RestControllerAdvice:仅添加Spring MVC框架后可使用

@Service:Service这种业务类的注解

@Repository:处理数据源中的数据读写的类的注解

以上4种组件注解在Spring框架作用范围之内是完全等效的

在Spring框架中,还有@Configuration注解,也是组件注解的一种

但是Spring对此注解的处理更加特殊(Spring框架对配置类使用了代理模式)

对于这2种创建对象的做法,通常:

如果是自定义的类,优先使用组件扫描的做法来创建对象

如果不是自定义的类,无法使用组件扫描的做法,只能在配置类中通过@Bean方法来创建对象

当Spring成功的创建了对象后,会将对象保存在Spring应用程序上下文(ApplicationContext)中

后续,当需要这些对象时,可以从Spring应用程序上下文中获取!

由于Spring应用程序上下文中持有大量对象的引用,所以,Spring应用程序上下文也通常被称之为"Spring容器"

Spring MVC框架--响应结果:默认情况下,处理请求的方法的返回值将表示"处理响应结果的视图组件的名称,及相关的数据",在

Spring MVC中,有一种内置的返回值类型"ModelAndView",不是前后端分离的做法

在处理请求的方法上,可以添加@ResponseBody注解,当添加此注解后,处理请求的方法的返回值将

表示响应的数据,不再由服务器端决定视图组件,也叫做响应正文,这是前后端分离的做法

@ResponseBody注解可以添加在方法和控制器类上

控制器类需要添加@Controller注解,才是控制器,或者也可以改为添加@RestController,此注解是由

@Controller和@ResponseBody组合而成的,所以添加@RestController后,当前控制器类中所有处理

请求的方法都是响应正文的

当控制器处理完请求需要响应正文时,Spring MVC会根据方法的返回值类型,来决定使用某个

MessageConverter来讲返回值转换为响应到客户端的数据

处理异常:添加了@ExceptionHandler注解的方法,就是处理异常的方法。

处理异常的方法到底处理哪种异常,由@ExceptionHandler注解参数或方法的参数中的异常类型来决定

如果@ExceptionHandler注解没有配置参数,由方法的参数中的异常类型决定,

如果@ExceptionHandler注解配置了参数,由以注解参数中配置的类型为准!

处理异常的方法可以声明在控制器类,将只作用于当前控制器类中的方法抛出的异常!

通常,建议将处理异常的方法声明在专门的类中,并在此类上添加@ControllerAdvice注解

当添加此注解后,此类中特定的方法(例如处理异常的方法)将作用于每次处理请求的过程中!

如果处理异常后的将"响应正文",也可以在处理异常的方法上添加@ResponseBody注解,

或在当前类上添加@ResponseBody,或使用@RestControllerAdvice取代@ControllerAdvice和@ResponseBody

Spring Boot框架-----作用:主要解决了统一管理依赖项与简化配置相关的问题

注解----注解所属框架作用

@ComponentScanSpring添加在配置类上,开启组件扫描。

如果没有配置包名,则扫描当前配置类所在的包,

如果配置了包名,则扫描所配置的包及其子孙包

@ComponentSpring添加在类上,标记当前类是组件类,可以通过参数配置Spring Bean名称

@ControllerSpring添加在类上,标记当前类是控制器组件类,用法同@Component

@ServiceSpring添加在类上,标记当前类是业务逻辑组件类,用法同@Component

@RepositorySpring添加在类上,标记当前类是数据访问组件类,用法同@Component

@ConfigurationSpring添加在类上,仅添加此注解的类才被视为配置类,通常不配置注解参数

@BeanSpring添加在方法上,标记此方法将返回某个类型的对象,

且Spring会自动调用此方法,并将对象保存在Spring容器中

@AutowiredSpring添加在属性上,使得Spring自动装配此属性的值

添加在构造方法上,使得Spring自动调用此构造方法

添加在Setter方法上,使得Spring自动调用此方法

@QualifierSpring添加在属性上,或添加在方法的参数上,

配合自动装配机制,用于指定需要装配的Spring Bean的名称

@ScopeSpring添加在组件类上,或添加在已经添加了@Bean注解的方法上,

用于指定作用域,注解参数为singleton(默认)时为“单例”,注解参数为prototype时为“非单例”

@LazySpring添加在组件类上,或添加在已经添加了@Bean注解的方法上,

用于指定作用域,当Spring Bean是单例时,注解参数为true(默认)时为“懒加载”,注解参数为false时为“预加载”

@ValueSpring添加在属性上,或添加在被Spring调用的方法的参数上,用于读取Environment中的属性值

@ResourceSpring此注解是javax包中的注解,

添加在属性上,使得Spring自动装配此属性的值,

通常不推荐使用此注解

@ResponseBodySpring MVC添加在方法上,标记此方法是“响应正文”的,

添加在类上,标记此类中所有方法都是“响应正文”的

@RestControllerSpring MVC添加在类上,标记此类是一个“响应正文”的控制器类

@RequestMappingSpring MVC添加在类上,也可以添加在处理请求的方法上,

通常用于配置请求路径

@GetMappingSpring MVC添加在方法上,是将请求方式限制为GET的@RequestMapping

@PostMappingSpring MVC添加在方法上,是将请求方式限制为POST的@RequestMapping

@DeleteMappingSpring MVC添加在方法上,是将请求方式限制为DELETE的@RequestMapping

@PutMappingSpring MVC添加在方法上,是将请求方式限制为PUT的@RequestMapping

@RequestParamSpring MVC添加在请求参数上,可以:

1. 指定请求参数名称

2. 要求必须提交此参数

3. 指定请求参数的默认值

@PathVariableSpring MVC添加在请求参数上,用于标记此参数的值来自URL中的占位符,如果URL中的占位符名称与方法的参数名称不同,需要配置此注解参数来指定URL中的占位符名称

@RequestBodySpring MVC添加在请求参数上,用于标记此参数必须是对象格式的参数,如果未添加此注解,参数必须是FormData格式的

@ExceptionHandlerSpring MVC添加在方法上,标记此方法是处理异常的方法,可以通过配置注解参数来指定需要处理的异常类型,如果没有配置注解参数,所处理的异常类型取决于方法的参数列表中的异常类型

@ControllerAdviceSpring MVC添加在类上,标记此类中特定的方法将作用于每次处理请求的过程中

@RestControllerAdviceSpring MVC添加在类上,是@ControllerAdvice和@ResponseBody的组合注解

@MapperScanMybatis添加在配置类上,用于指定Mapper接口的根包,Mybatis将根据此根包执行扫描,以找到各Mapper接口

@MapperMybatis添加在Mapper接口上,用于标记此接口是Mybatis的Mapper接口,如果已经通过@MapperScan配置能够找到此接口,则不需要使用此注解

@ParamMybatis添加在Mapper接口中的抽象方法的参数上,用于指定参数名称,当使用此注解指定参数名称后,SQL中的#{} / ${}占位符中的名称必须是此注解指定的名称,通常,当抽象方法的参数超过1个时,强烈建议在每个参数上使用此注解配置名称

@SelectMybatis添加在Mapper接口的抽象方法上,可以通过此注解直接配置此抽象方法对应的SQL语句(不必将SQL语句配置在XML文件中),用于配置SELECT类的SQL语句,但是,非常不推荐这种做法

@InsertMybatis同上,用于配置INSERT类的SQL语句

@UpdateMybatis同上,用于配置UPDATE类的SQL语句

@DeleteMybatis同上,用于配置DELETE类的SQL语句

@TransactionalSpring JDBC推荐添加在业务接口上,用于标记此接口中所有方法都是事务性的,或业务接口中的抽象方法上,用于此方法是事务性的

@SpringBootApplicationSpring Boot添加在类上,用于标记此类是Spring Boot的启动类,每个Spring Boot项目应该只有1个类添加了此注解

@SpringBootConfigurationSpring Boot通常不需要显式的使用,它是@SpringBootApplication的元注解之一

@SpringBootTestSpring Boot添加在类上,用于标记此类是加载Spring环境的测试类

@ValidSpring Validation添加在方法的参数上,标记此参数需要经过Validation框架的检查

@ValidatedSpring Validation添加在方法的参数上,标记此参数需要经过Validation框架的检查;添加在类上,并结合方法上的检查注解(例如@NotNull等)实现对未封装的参数的检查

@NotNullSpring Validation添加在需要被检查的参数上,或添加在需要被检查的封装类型的属性上,用于配置“不允许为null”的检查规则

@NotEmptySpring Validation使用位置同@NotNull,用于配置“不允许为空字符串”的检查规则

@NotBlankSpring Validation使用位置同@NotNull,用于配置“不允许为空白”的检查规则

@PatternSpring Validation使用位置同@NotNull,用于配置正则表达式的检查规则

@RangeSpring Validation使用位置同@NotNull,用于配置“数值必须在某个取值区间”的检查规则

@ApiKnife4j添加在控制器类上,通过此注解的tags属性配置API文档中的模块名称

@ApiOperationKnife4j添加在控制器类中处理请求的方法上,用于配置业务名称

@ApiOperationSupportKnife4j添加在控制器类中处理请求的方法上,通过此注解的order属性配置业务显示在API文档中时的排序序号

@ApiModelPropertyKnife4j添加在封装的请求参数类型中的属性上,用于配置请求参数的详细说明,包括:名称、数据类型、是否必须等

@ApiImplicitParamKnife4j添加在控制器类中处理请求的方法上,用于配置请求参数的详细说明,包括:名称、数据类型、是否必须等

@ApiImplicitParamsKnife4j添加在控制器类中处理请求的方法上,如果需要通过@ApiImplicitParam注解配置的参数超过1个,则必须将多个@ApiImplicitParam注解作为此注解的参数

@ApiIgnoreKnife4j添加在请求参数上,用于标记API文档中将不关心此参数

@EnableGlobalMethodSecuritySpring Security添加在配置类上,用于开启全局的方法级别的权限控制

@PreAuthorizeSpring Security添加在方法上,用于配置权限

@AuthenticationPrincipalSpring Security添加在方法的参数上,且此参数应该是Security上下文中的认证信息中的当事人类型,用于为此参数注入值

@DataLombok添加在类上,将在编译期生成此类中所有属性的Setter、Getter方法,及hashCode()、equals()、toString()方法

@SetterLombok添加在类上,将在编译期生成此类中所有属性的Setter方法,也可以添加在类的属性上,将在编译期生成此属性的Setter方法

@GetterLombok添加在类上,将在编译期生成此类中所有属性的Getter方法,也可以添加在类的属性上,将在编译期生成此属性的Getter方法

@EqualsAndHashcodeLombok添加在类上,将在编译期生成基于此类中所有属性的hashCode()、equals()方法

@ToStringLombok添加在类上,将在编译期生成基于此类中所有属性的toString()方法

@NoArgConstructorLombok添加在类上,将在编译期生成此类的无参数构造方法

@AllArgsConstructorLombok添加在类上,将在编译期生成基于此类中所有属性的全参构造方法

Redis---是一款基于内存来读写数据的NoSQL非关系型数据库,Redis访问的数据都在内存中,其实Redis也会自动的处理持久化

但是正常读写都是在内存中执行的

NoSQL:不涉及SQL语句,也可理解为No Operation(不操作)

非关系型数据库:不关心数据库中存储的是什么数据,几乎没有数据种类的概念,更不存在数据与数据之间的关联

通常,在项目中,Redis用于实现缓存

优点:读取数据的效率会高很多

能够一定程度上保障缓解数据库的查询压力,提高数据库的安全性

缺点:需要关注数据一致性问题,即Redis中的数据与MySQL中的数据是否一致,如果不一致,是否需要处理

Redis的基本操作-----在Windows操作系统中,通过.msi安装包来安装的Redis,会自动注册Redis服务,开机会自动启动Redis,所以

Redis处于随时可用的状态

redis传统数据类型:

string 字符串

list 列表

hash 对象

set 集合

zset 有序集合

常用命令:

redis-cli 登录redis控制台

exit 退出redis

ping 检查redis是否可用

set key value 存值

get key 取值

flushall 清除所有数据

del key 删除指定数据

dbsize 查看数据总数

keys pattern 根据模式查找key,通常不建议使用

添加依赖项:

org.springframework.boot

spring-boot-starter-data-redis

在配置类RedisConfiguration中做出如下配置:

@Bean

public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){

RedisTemplate redisTemplate = new RedisTemplate<>();

redisTemplate.setConnectionFactory(redisConnectionFactory);

redisTemplate.setKeySerializer(RedisSerializer.string());

redisTemplate.setValueSerializer(RedisSerializer.json());

return redisTemplate;

}

数据一致性问题的思考----当使用Redis缓存数据,如果数据库中的数据发生了变化,此时,Redis中的数据会暂时与数据库中的

数据并不相同,通常称之为"数据一致性问题",一般有两种做法:

及时更新Redis缓存数据

放任数据不一致的表现,直至更新Redis数据为止

如果及时更新Redis缓存数据,其优点是Redis缓存中的数据基本上是准确的,其缺点在于可能需要频繁

的更新Redis缓存数据,本质上是反复读MySQL、反复写Redis的操作,如果读取Redis数据的频率根

本不高,则会形成浪费,并且更新缓存的频率太高也会增加服务器的压力,所以,对于增、删、改

频率非常高的数据,可能不太适用此规则

放任数据不一致的表现,其缺点很显然就是数据可能不准确,其优点是没有给服务器端增加任何压力

其实并不是所有的数据都必须时时刻刻都要求准确的,某些数据即使不准确,也不会产生恶劣后果

例如热门话题排行榜,定期更新即可,或者某些数据即使准确,也没有太多实际意义,例如热门时段

的火车票、飞机票等,就算在列表中显示了正确的余量,也不一定能够成功购买

通常的规律:

使用Redis缓存的数据符合一定标准-

数据的增、删、改的频率非常低,查询频率更高。这时无论采取即时还是定期更新策略都是可行的

例如电商平台中的商品类别

对数据的准确性要求不高的数据,例如某些列表或榜单,使用定期更新策略,更新周期根据数据

变化的频率及其价值来决定

Redis缓存数据的操作-----通常,推荐将Redis的读写数据操作进行封装

在根包下创建repo.IBrandRedisRepository接口

在repo包下创建impl.BrandRedisRepositoryImpl实现类,实现以上接口

开机之后马上进行缓存读写操作----缓存预热,在启动项目的时候就将缓存数据加载到Redis缓存中

在Spring Boot项目中,自定义组件类,实现ApplicationRunner接口,此接口中的run()方法

会在启动项目之后立即执行,可以通过此机制实现缓存预热

周期性更新缓存的操作----计划任务,设定某种规则(通常是与时间相关的规则),当满足规则时,自动执行任务,并且,此规则可

能是周期性的满足,则任务也会周期性的执行

在Spring Boot项目中,需要在配置类上添加@EnableScheduling注解,以开启计划任务,否则,当前项目

中所有计划任务都是不允许执行的

在任何组件类中,自定义方法,在方法上添加@Scheduled注解,则此方法就是计划任务,通过此注解的

参数可以配置计划任务的执行规则(参数:fixedRate = 500, fixedDelay = 500)

计划任务在项目启动时就会执行第一次

Mybatis的缓存机制---Mybatis框架默认是有2套缓存机制的,分别称之一级缓存和二级缓存

一级缓存也称之为"会话(Session)缓存",默认是开启的,且无法关闭

一级缓存必须保证多次的查询操作满足:同一个SqlSession、同一个Mapper、执行相同的SQL查询、使用相同的参数

测试代码:SqlSession sqlSession = sqlSessionFactory.openSession()

一级缓存会因为以下任意一种原因而消失:

手动清除缓存:sqlSession.clearCache()

当前执行了任何写操作

二级缓存也称之为"namespace缓存",是作用于某个namespace的,具体表现为:无论是否为同一个SqlSession

只要执行的是相同的Mapper的查询,且查询参数相同,就可以应用二级缓存

在使用Spring Boot与Mybatis的项目中,二级缓存默认是全局开启的,但各namespace默认并未开启

如果需要在namespace中开启二级缓存,需要在XML文件中添加标签

则表示当前XML中所有查询都开启了二级缓存

需要注意:使用二级缓存时,需要保证查询结果的类型实现了Serializable接口

另外,还可以在