Spring全家桶之SpringBoot——高级阶段

SpringBoot

  • 介绍
    • 前提
    • 简介
    • 学习导图
  • 一、服务端表单数据校验
    • 需求
    • 环境搭建
    • 主要代码
      • 视图层
      • Controller层
        • 方式一(注入对象式)
        • 方式二(@ModelAttribute()注解式)
      • 实体类
    • 表单校验常用注解总结
  • 二、SpringBoot中异常处理的方式
    • 自定义错误页面
    • @ExceptionHandle 注解处理异常
    • @ControllerAdvice + @ExceptionHandle 注解处理异常
    • 配置SimpleMappingExceptionResolver 处理异常
    • 自定义 HandlerExceptionResolver 类处理异常
  • 三、Spring Boot 整合Junit 单元测试
    • 步骤
      • 创建jar 项目,修改pom文件
      • 测试类
      • 启动类
  • 四、SpringBoot 热部署
    • 1. SpringLoader 插件的使用
      • 修改pom文件
      • 使用maven 的命令起来启动
        • SpringLoader 缺陷
        • 补充 : 如何使用命令行杀死进程
      • 使用jar 包的方式
        • 添加springloader 的jar 包
        • 启动方式
    • 2. DevTools 工具的使用
      • SpringLoader 与DevTools 的区别
      • 使用方式
        • 修改项目的pom 文件添加devtools 的依赖
        • 运行启动类即可
  • 五、Spring Boot 整合Spring Data JPA
    • Spring Data JPA 介绍
    • Spring Boot 整合Spring Data JPA
      • 创建项目 ,修改pom文件
      • 在项目中添加application.properties 文件
      • 添加实体类
      • 编写Dao 接口
      • 创建启动类
      • 测试类代码
      • Spring Data JPA 提供的核心接口
      • 1. Repository 接口
          • 接口类
        • 方法名称命名查询方式
          • 测试代码
        • 基于@Query 注解查询与更新
          • 测试代码
      • 2. CrudRepository 接口
        • 接口类
        • 测试代码
      • 3. PagingAndSortingRepository 接口
        • 接口类
        • 测试代码
      • 4. JpaRepository 接口
        • 接口类
        • 测试代码
      • 5. JPASpecificationExecutor 接口
        • 接口类
        • 测试代码
    • 关联映射操作
      • 环境搭建
      • 一对多的关联关系
        • 实体类-角色
        • 实体类-用户
        • 接口类
        • 测试代码
      • 多对多关系
        • 实体类-角色
        • 实体类-菜单
        • 接口类
        • 测试代码
  • 六、Spring Boot 整合Ehcache
    • 整合步骤
      • 修改pom文件
      • 创建Ehcache 的配置文件
      • 修改application.properties 文件
      • 修改启动类
    • 功能测试
      • dao层
      • 业务层
      • 测试代码
    • 常用注解解析
  • 七、整合Redis
    • 导入maven依赖
    • redis参数配置文件application.properties
    • redis的配置类
    • 实体类
    • 启动类
    • 测试类
  • 八、 定时任务
    • 1. Scheduled
      • 项目框架搭建
      • cron 表达式讲解
    • 2. Quartz定时任务框架
      • Quartz介绍
      • 项目框架搭建
      • 整合SpringBoot

介绍

前提

• 学习过SpringData Jpa课程
• 学习过SpringData Redis课程
• 学习过 SpringBoot 初级阶段

简介

Spring Boot是一个简化Spring开发的框架。用来监护spring应用开发,约定大于配置,去繁就简,just run 就能创建一个独立的,产品级的应用。我们在使用Spring Boot时只需要配置相应的Spring Boot就可以用所有的Spring组件,简单的说,spring boot就是整合了很多优秀的框架,不用我们自己手动的去写一堆xml配置然后进行配置。从本质上来说,Spring Boot就是Spring,它做了那些没有它你也会去做的Spring Bean配置。

学习导图

Spring全家桶之SpringBoot——高级阶段_第1张图片

一、服务端表单数据校验

需求

搭建一个简单的环境 ,实现表单的校验 .包括对用户名 ,密码 ,年龄 ,邮箱等进行校验.

环境搭建

见本人上篇博文 SpringBoot初级阶段之SpringBoot 整合SpringMVC+MyBatis

主要代码

视图层

注意 : th:errors 会获取响应的数据 .有,会将数据取出,没有会报异常

<font color="red" th:errors="${users.name}">font>

add.html


<html>
<head>
<meta charset="UTF-8">
<title>Insert title heretitle>
head>
<body>

	<form th:action="@{/save}" method="post">
		用户姓名:<input type="text" name="name" />
		<font color="red" th:errors="${users.name}">font><br /> 
		用户密码:<input type="password" name="password" />
		<font color="red" th:errors="${users.password}">font><br />
		用户年龄:<input type="text" name="age" />
		<font color="red" th:errors="${users.age}">font><br /> 
		用户邮箱:<input type="text" name="email" />
		<font color="red" th:errors="${users.email}">font><br /> 
		<input type="submit" value="添加" />
	form>
body>
html>

Controller层

方式一(注入对象式)

步骤
1.在页面的跳转的Controller方法中注入需要校验的对象
2.在添加对象的Controller方法中在该对象的参数旁加 @Valid注解
3.在添加对象的Controller方法中加入BindingResult的对象 result ,用于封装校验结果
4.对结果进行判断 ,没有有错误就重新返回该页面 if (result.hasErrors()) {return "add"; }

@Controller
public class UsersController {
	/**
	 * 解决异常的方式。可以在跳转页面的方法中注入一个Uesrs 对象。 
	 * 注意:由于springmvc 会将该对象放入到Model 中传递。key 的名称会使用
	 * 该对象的驼峰式的命名规则来作为key。 参数的变量名需要与对象的名称相同。将首字母小写。
	 *
	 * @param users
	 * @return
	 */
	@RequestMapping("/addUser")
	public String addUser(Users users) {
		return "add";
	}

	/**
	 * 完成用户添加
	 * 
	 * @Valid 开启对Users 对象的数据校验
	 * @param result 封装了校验的结果
	 * @return
	 */
	@RequestMapping("/save")
	public String saveUser(@Valid Users users, BindingResult result) {
		if (result.hasErrors()) {
			return "add";
		}
		System.out.println(users);
		return "ok";
	}
}

方式二(@ModelAttribute()注解式)

步骤
1.在页面的跳转的Controller方法中注入需要校验的对象 并加入@ModelAttribute()注解
2.在添加对象的Controller方法中在该对象的参数旁加 @Valid注解 ,在其前方加 @ModelAttribute()注解
3.在添加对象的Controller方法中加入BindingResult的对象 result ,用于封装校验结果
4.对结果进行判断 ,没有有错误就重新返回该页面 if (result.hasErrors()) {return "add"; }
5.总结 :@ModelAttribute() 注解的作用相当于为 users 对象修改别名 ,使用后,前端获取方式应该同样改变,类似下面这样

  用户姓名:<input type="text" name="name" />
    <font color="red" th:errors="${aa.name}">font><br /> 
@Controller
public class UsersController {
	/**
	 * 解决异常的方式。可以在跳转页面的方法中注入一个Uesrs 对象。 
	 * 注意:由于springmvc 会将该对象放入到Model 中传递。key 的名称会使用
	 * 该对象的驼峰式的命名规则来作为key。 参数的变量名需要与对象的名称相同。将首字母小写。
	 *
	 * @param users
	 * @return
	 */
	@RequestMapping("/addUser")
	public String addUser(@ModelAttribute("aa")Users users) {
		return "add";
	}

	/**
	 * 完成用户添加
	 * 
	 * @Valid 开启对Users 对象的数据校验
	 * @param result 封装了校验的结果
	 * @return
	 */
	@RequestMapping("/save")
	public String saveUser(@ModelAttribute("aa")@Valid Users users, BindingResult result) {
		if (result.hasErrors()) {
			return "add";
		}
		System.out.println(users);
		return "ok";
	}
}

实体类

实现表单校验最根本的一步就是要在实体类上加入相应的注解

public class Users {
	@NotBlank(message="用户名不能为空")  //非空校验
	@Length(min=3,max=6)
	private String name;
	@NotBlank(message="密码不能为空")  //密码非空校验
	private String password;
	@Min(value=15,message="年龄不能低于15")
	private Integer age;
	//相关方法省略
}

表单校验常用注解总结

注解名称 作用
@NotBlank 判断字符串是否为null 或者是空串(去掉首尾空格)。
@NotEmpty: 判断字符串是否null 或者是空串。
@Length 判断字符的长度(最大或者最小)
@Min 判断数值最小值
@Max 判断数值最大值
@Email 判断邮箱是否合法

注意 :

  1. 可以在每个注解中通过message自定义表单校验异常信息
  2. 更多表单验证请看这里
    https://www.jb51.net/article/122779.htm

二、SpringBoot中异常处理的方式

自定义错误页面

SpringBoot 默认的处理异常的机制:
SpringBoot 默认的已经提供了一套处理异常的机制。一旦程序中出现了异常SpringBoot 会向 /error 的url 发送请求。在springBoot 中提供了一个叫BasicExceptionController 来处理 /error 请求,然后跳转到默认显示异常的页面来展示异常信息。

Spring全家桶之SpringBoot——高级阶段_第2张图片

如果我们需要将所有的异常同一跳转到自定义的错误页面, 需要在 src/main/resources/templates 目录下创建error.html 页面。注意:名称必须叫error
error.html


<html>
<head>
<meta charset="UTF-8">
<title>Insert title heretitle>
head>
<body>
错误了...请自我检讨~~~
body>
html>

Controller层

@Controller
public class ThymeleafController {
	
	@RequestMapping("/show")
	public String showOther() {
		
		int i=0;
		System.out.println(i/0);
		return "index";//index 为任意html页面
	}
}

Spring全家桶之SpringBoot——高级阶段_第3张图片

@ExceptionHandle 注解处理异常

它会捕获注解里面的异常 ,然后通过Controller跳转到相应的异常页面 ,例如下面一行代码就会捕获算术异常
@ExceptionHandler(value = { java.lang.ArithmeticException.class })

error1.html


<html>
<head>
<meta charset="UTF-8">
<title>错误提示页面-ArithmeticExceptiontitle>
head>
<body>
	出错了,请与管理员联系。。。
	<span th:text="${error}">span>
body>
html>

error2.html


<html>
<head>
<meta charset="UTF-8">
<title>错误提示页面-NullPointerExceptiontitle>
head>
<body>
	出错了,请与管理员联系。。。
	<span th:text="${error}">span>
body>
html>

controller

@Controller
public class ThymeleafController {

	@RequestMapping("/show")
	public String showInfo() {
		String str = null;
		str.length();
		return "index";
	}

	@RequestMapping("/show2")
	public String showOther() {

		int i = 0;
		System.out.println(i / 0);
		return "index";
	}

	/**
	 * java.lang.ArithmeticException 该方法需要返回一个ModelAndView:
	 * 目的是可以让我们封装异常信息以及视 图的指定
	 * 参数Exception e:会将产生异常对象注入到方法中
	 */
	@ExceptionHandler(value = { java.lang.ArithmeticException.class })
	public ModelAndView arithmeticExceptionHandler(Exception e) {
		ModelAndView mv = new ModelAndView();
		mv.addObject("error", e.toString());
		mv.setViewName("error1");
		return mv;
	}
	
	
	/**
	* java.lang.NullPointerException
	* 该方法需要返回一个ModelAndView:目的是可以让我们封装异常信息以及视图的指定
	* 参数Exception e:会将产生异常对象注入到方法中
     */
	@ExceptionHandler(value={java.lang.NullPointerException.class})
		public ModelAndView nullPointerExceptionHandler(Exception e){
		ModelAndView mv = new ModelAndView();
		mv.addObject("error", e.toString());
		mv.setViewName("error2");
		return mv;
	}


}

@ControllerAdvice + @ExceptionHandle 注解处理异常

这一种方式是对前两种方式的升华 ,但是问题仍是代码量过多 ,可以实现错误页面的映射 ,以及所有对应异常的页面的跳转

/**
 * 全局异常处理类
 * 使用@ControllerAdvice配合@ExceptionHandler
 * 用于在一个类中定义全局异常
 * 
 * @author chy
 *
 */
@ControllerAdvice
public class GlobalException {
	
	/**
	 * java.lang.ArithmeticException 该方法需要返回一个ModelAndView:
	 * 目的是可以让我们封装异常信息以及视 图的指定
	 * 参数Exception e:会将产生异常对象注入到方法中
	 */
	@ExceptionHandler(value = { java.lang.ArithmeticException.class })
	public ModelAndView arithmeticExceptionHandler(Exception e) {
		ModelAndView mv = new ModelAndView();
		mv.addObject("error", e.toString());
		mv.setViewName("error1");
		return mv;
	}
	
	
	/**
	* java.lang.NullPointerException
	* 该方法需要返回一个ModelAndView:目的是可以让我们封装异常信息以及视图的指定
	* 参数Exception e:会将产生异常对象注入到方法中
     */
	@ExceptionHandler(value={java.lang.NullPointerException.class})
		public ModelAndView nullPointerExceptionHandler(Exception e){
		ModelAndView mv = new ModelAndView();
		mv.addObject("error", e.toString());
		mv.setViewName("error2");
		return mv;
	}


}

配置SimpleMappingExceptionResolver 处理异常

/**
 * SimpleMappingExceptionResolver处理异常
 * 在全局异常类中添加一个方法@Bean  完成异常的统一处理
 * 但是缺陷和第二种一样,无法对每个Controller的异常都进行对应的跳转
 * 
 * @author chy
 *
 */
@Configuration
public class GlobalException {

	/**
	 * 该方法必须要有返回值。返回值类型必须是: SimpleMappingExceptionResolver
	 */
	@Bean
	public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver() {
		SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
		Properties mappings = new Properties();
		/**
		 * 参数一:异常的类型,注意必须是异常类型的全名 参数二:视图名称
		 */
		mappings.put("java.lang.ArithmeticException", "error1");
		mappings.put("java.lang.NullPointerException", "error2");
		// 设置异常与视图信息的映射
		resolver.setExceptionMappings(mappings);
		return resolver;
	}
}

自定义 HandlerExceptionResolver 类处理异常

/**
 * 自定义HandlerExceptionResolver 类处理异常 
 * 需要再全局异常处理类中实现HandlerExceptionResolver 接口
 * 同样配合 @Configuration实现全局异常的页面映射与跳转 这种方式是对第四种的优化 ,
 * 弥补了第四种只建立了映射关系 ,没有显示对应异常的现象 ,强烈推荐使用!!!
 * 
 * @author chy
 *
 */
@Configuration
public class GlobalException implements HandlerExceptionResolver {

	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
			Exception ex) {
		ModelAndView mv = new ModelAndView();
		// 判断不同异常类型,做不同视图跳转
		if (ex instanceof ArithmeticException) {
			mv.setViewName("error1");
		}
		if (ex instanceof NullPointerException) {
			mv.setViewName("error2");
		}
		//将异常信息封装成相应作用域对象,发送给视图层
		mv.addObject("error", ex.toString());
		return mv;
	}

}

三、Spring Boot 整合Junit 单元测试

步骤

创建jar 项目,修改pom文件

,继承父启动器 ,添加web启动器之后 ,需要额外添加用于支持一个单元测试的启动器

	<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>1.5.10.RELEASEversion>
  parent>

	
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
		dependency>

测试类

1. 在测试类头部需要加上 ,下面两个注解 ,并创建启动类
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = { AppForJunit.class })
2. 在被测试的方法上加上@Test注解

/**
 * SpringBoot 测试类
 * 
 * @RunWith:启动器 SpringJUnit4ClassRunner.class:让junit 与spring 环境进行整合
 * @SpringBootTest(classes={AppForJunit.class}) 注解的两个功能
 * 1. 当前类为springBoot 的测试类
 * 2. 加载SpringBoot 启动类。启动 springBoot
 *
 * junit 与spring
    *     整合 @Contextconfiguartion("classpath:applicationContext.xml")
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = { AppForJunit.class })
public class TestJunit {
	@Autowired
	private UsersService usersService;

	@Test
	public void addUser() {
		this.usersService.adduser();
	}
}

启动类

@SpringBootApplication
public class AppForJunit {
	
	public static void main(String[] args) {
		SpringApplication.run(AppForJunit.class, args);
	}
}

测试结果
Spring全家桶之SpringBoot——高级阶段_第4张图片

四、SpringBoot 热部署

1. SpringLoader 插件的使用

复制一个SpringBoot的web项目

修改pom文件

添加SpringLoader 插件


	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
				<dependencies>
					<dependency>
						<groupId>org.springframeworkgroupId>
						<artifactId>springloadedartifactId>
						<version>1.2.5.RELEASEversion>
					dependency>
				dependencies>
			plugin>
		plugins>
	build>

使用maven 的命令起来启动

右击项目 run as->Maven Builder…输入以下命令

spring-boot:run

Spring全家桶之SpringBoot——高级阶段_第5张图片

SpringLoader 缺陷

  1. 就是Java 代码做部署处理。但是对页面无能为力。
  2. 这种方式的缺点是Springloader 热部署程序是在系统后台以进程的形式来运行。需要手动关闭该进程
    在这里插入图片描述

补充 : 如何使用命令行杀死进程

  1. 输入 tasklist ,会显示正在运行的进程以及他们的pid ,找到对应的pid
    在这里插入图片描述
  2. 找到需要结束进程的pid编号,输入taskkill /pid 编号 /f,按回车键,如下图所示:

Spring全家桶之SpringBoot——高级阶段_第6张图片
3. 也可以通过进程的名称来结束进程,输入taskkill /im 进程名称(带上后缀) /f 就可以关闭进程了,

使用jar 包的方式

添加springloader 的jar 包

Spring全家桶之SpringBoot——高级阶段_第7张图片

启动方式

Spring全家桶之SpringBoot——高级阶段_第8张图片
Spring全家桶之SpringBoot——高级阶段_第9张图片

启动命令:
-javaagent:.\lib\springloaded-1.2.5.RELEASE.jar -noverify

2. DevTools 工具的使用

SpringLoader 与DevTools 的区别

SpringLoader:SpringLoader 在部署项目时使用的是热部署的方式。
DevTools:DevTools 在部署项目时使用的是重新部署的方式

使用方式

修改项目的pom 文件添加devtools 的依赖


		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-devtoolsartifactId>
			<optional>trueoptional>
		dependency>

运行启动类即可

在进行热部署时 ,推荐使用 DevTools 工具 ,这样在前端和后端代码都改变时都能及时看到结果

五、Spring Boot 整合Spring Data JPA

Spring全家桶之SpringBoot——高级阶段_第10张图片

Spring Data JPA 介绍

Spring Data:其实Spring Data 就是spring 提供了一个操作数据的框架。而Spring Data JPA只是Spring Data 框架下的一个基于JPA 标准操作数据的模块。
Spring Data JPA:基于JPA 的标准对数据进行操作。简化操作持久层的代码。只需要编
写接口就可以。

Spring Boot 整合Spring Data JPA

创建项目 ,修改pom文件

  1. 继承SpringBoot 启动器的父项目
  2. 添加部署tomcat,使用thymeleaf进行开发,采取Jpa规范和单元测试的启动器
  3. 添加jdbc的jar 以及数据库连接池jar的坐标
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>1.5.10.RELEASEversion>
	parent>
	<groupId>ah.szxy.SpringBootgroupId>
	<artifactId>22-SpringBoot-JpaartifactId>
	<version>0.0.1-SNAPSHOTversion>
	

	<dependencies>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-thymeleafartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-data-jpaartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
		dependency>


		
		<dependency>
			<groupId>mysqlgroupId>
			<artifactId>mysql-connector-javaartifactId>
		dependency>
		
		<dependency>
			<groupId>com.alibabagroupId>
			<artifactId>druidartifactId>
			<version>1.0.9version>
		dependency>
	dependencies>

project>

在项目中添加application.properties 文件

#连接数据库
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ssm?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
#指定连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

#开启正向工程
spring.jpa.hibernate.ddl-auto=update
#运行时,打印sql语句
spring.jpa.show-sql=true

添加实体类

注意相关注解的使用

@Entity
@Table(name="t_users")
public class Users {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="id")
	private Integer id;
	@Column(name="name")
	private String name;
	@Column(name="age")
	private Integer age;
	@Column(name="address")
	private String address;
	//相关方法省略
	
}

编写Dao 接口

/**
* 参数一T :当前需要映射的实体
* 参数二ID :当前映射的实体中的OID 的类型
*
*/
public interface UsersRepository extends JpaRepository<Users, Integer>{

}

创建启动类

@SpringBootApplication
public class AppForSpringBootJap {
	public static void main(String[] args) {
		SpringApplication.run(AppForSpringBootJap.class, args);
	}
}

测试类代码

测试类所导入的所有的包,(因为会出现许多相同的包)

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import ah.szxy.AppForSpringBootJap;
import ah.szxy.dao.UsersRepository;
import ah.szxy.dao.UsersRepositoryByName;
import ah.szxy.dao.UsersRepositoryCrudRepository;
import ah.szxy.dao.UsersRepositoryPagingAndSorting;
import ah.szxy.dao.UsersRepositoryQueryAnnotation;
import ah.szxy.pojo.Users;

所注入的接口

@Autowired
	private UsersRepository userDao;
	@Autowired
	private UsersRepositoryByName usersRepositoryByName;
	@Autowired
	private UsersRepositoryQueryAnnotation usersRepositoryQueryAnnotation;
	@Autowired
	private UsersRepositoryCrudRepository usersRepositoryCrudRepository;
	@Autowired
	private UsersRepositoryPagingAndSorting usersRepositoryPagingAndSorting;

测试代码

@Test
	public void addUser() {
		Users users = new Users();
		users.setName("王晓红");
		users.setAge(20);
		users.setAddress("北京大兴区");
		this.userDao.save(users);
	}

Spring Data JPA 提供的核心接口

1. Repository 接口

接口类
	//方法的名称必须要遵循驼峰式命名规则。findBy(关键字)+属性名称(首字母要大写)+查询条件(首字母大写)
	/**
	 * 根据姓名查询
	 * @param name
	 * @return
	 */
	List<Users> findByName(String name);
	/**
	 * 根据姓名和年龄查询
	 * @param name
	 * @param age
	 * @return
	 */
	List<Users> findByNameAndAge(String name,Integer age);
	/**
	 * 姓名模糊查询
	 * @param name
	 * @return
	 */
	List<Users> findByNameLike(String name);
}

方法名称命名查询方式

测试代码
@Test
	public void findUserByName() {

		List<Users> list = this.usersRepositoryByName.findByName("周杰伦");
		for (Users users : list) {
			System.out.println(users);
		}
	}

	@Test
	public void findUserByNameAndAge() {

		List<Users> list = this.usersRepositoryByName.findByNameAndAge("周杰伦", 23);
		for (Users users : list) {
			System.out.println(users);
		}
	}

	@Test
	public void findUserByNamelike() {

		List<Users> list = this.usersRepositoryByName.findByNameLike("周%");
		for (Users users : list) {
			System.out.println(users);
		}
	}

基于@Query 注解查询与更新

注意 :

  1. 使用HQL语句与使用SQL语句在注解上的区别
  2. 更新需要开启事务 ,关闭回滚
测试代码
@Test
	public void findUserByNameHQL() {

		List<Users> list = this.usersRepositoryQueryAnnotation.queryByNameUseHQL("周杰伦");
		for (Users users : list) {
			System.out.println(users);
		}
	}

	@Test
	public void findUserByNameSQL() {

		List<Users> list = this.usersRepositoryQueryAnnotation.queryByNameUseSQL("周杰伦");
		for (Users users : list) {
			System.out.println(users);
		}
	}

	/**
	 * 再进行更新操作时,需要开启事务 ,关闭自动回滚
	 */
	@Test
	@Transactional
	@Rollback(false)
	public void updateUserByid() {

		this.usersRepositoryQueryAnnotation.updateUsersNameById("王小王", 4);
	}

2. CrudRepository 接口

CrudRepository 接口,主要是完成一些增删改查的操作。
注意:CredRepository 接口继承Repository 接口, 所能调用的方法如下
Spring全家桶之SpringBoot——高级阶段_第11张图片

接口类

public interface UsersRepositoryCrudRepository extends CrudRepository<Users, Integer>{

}

测试代码

/**
	 * CrudRepository 测试 save 方法中,若指定id ,则相当于更新操作, nice!!!
	 */
	@Test
	public void testCrudRepositorySave() {
		Users user = new Users();
		user.setId(7);
		user.setAddress("天津");
		user.setAge(20);
		user.setName("王小妹");
		this.usersRepositoryCrudRepository.save(user);
	}

	/**
	 * CrudRepository 测试-更新
	 * 
	 */
	@Test
	public void testCrudRepositoryUpdate() {
		Users user = new Users();
		user.setId(7);
		user.setAddress("阿拉德");
		user.setAge(40);
		user.setName("王大强");
		this.usersRepositoryCrudRepository.save(user);
	}

	/**
	 * CrudRepository 测试-查询(通过主键)
	 * 
	 */
	@Test
	public void testCrudRepositoryQuery() {

		Users user = this.usersRepositoryCrudRepository.findOne(4);
		System.out.println(user);
	}

	/**
	 * CrudRepository 测试-删除(通过主键)
	 * 
	 */
	@Test
	public void testCrudRepositoryDelete() {

		this.usersRepositoryCrudRepository.delete(6);
	}

3. PagingAndSortingRepository 接口

该接口提供了分页与排序的操作。
注意: 该接口继承了CrudRepository 接口

接口类

public interface UsersRepositoryPagingAndSorting extends PagingAndSortingRepository<Users, Integer> {

}

测试代码

	/**
	 * PagingAndSortingRepository 排序测试
	 */
	@Test
	public void testPagingAndSortingRepositorySort() {
		// Order 定义排序规则
		Order order = new Order(Direction.DESC, "id");
		// Sort 对象封装了排序规则
		Sort sort = new Sort(order);
		List<Users> list = (List<Users>) this.usersRepositoryPagingAndSorting.findAll(sort);
		for (Users users : list) {
			System.out.println(users);
		}
	}

	/**
	 * PagingAndSortingRepository 分页测试
	 */
	@Test
	public void testPagingAndSortingRepositoryPaging() {
		// Pageable:封装了分页的参数,当前页,每页显示的条数。注意:他的当前页是从0 开始。
		// PageRequest(page,size) page:当前页。size:每页显示的条数
		Pageable pageable = new PageRequest(1, 2);
		Page<Users> page = this.usersRepositoryPagingAndSorting.findAll(pageable);
		System.out.println("总条数:" + page.getTotalElements());
		System.out.println("总页数" + page.getTotalPages());
		List<Users> list = page.getContent();
		for (Users users : list) {
			System.out.println(users);
		}
	}

	/**
	 * PagingAndSortingRepository 排序+分页
	 */
	@Test
	public void testPagingAndSortingRepositorySortAndPaging() {
		Sort sort = new Sort(new Order(Direction.DESC, "id"));
		Pageable pageable = new PageRequest(1, 2, sort);
		Page<Users> page = this.usersRepositoryPagingAndSorting.findAll(pageable);
		System.out.println("总条数:" + page.getTotalElements());
		System.out.println("总页数" + page.getTotalPages());
		List<Users> list = page.getContent();
		for (Users users : list) {
			System.out.println(users);
		}
	}

4. JpaRepository 接口

该接口继承了PagingAndSortingRepository 接口。对继承的父接口中的方法的返回值进行适配。
对比PagingAndSortingRepository ,在进行查询时无需对返回结果进行强转 ,方便我们编程

接口类

/**
* 参数一T :当前需要映射的实体
* 参数二ID :当前映射的实体中的OID 的类型
*
*/
public interface UsersRepository extends JpaRepository<Users, Integer>{

}

测试代码

/**
	 * PagingAndSortingRepository 排序测试
	 */
	@Test
	public void testPagingAndSortingRepositorySort() {
		// Order 定义排序规则
		Order order = new Order(Direction.DESC, "id");
		// Sort 对象封装了排序规则
		Sort sort = new Sort(order);
		List<Users> list = this.userDao.findAll(sort);
		for (Users users : list) {
			System.out.println(users);
		}
	}

5. JPASpecificationExecutor 接口

接口类

测试代码

相关代码见SpringData Jpa课程第九部分

关联映射操作

环境搭建

pom文件


	<dependencies>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-thymeleafartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-data-jpaartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
		dependency>


		
		<dependency>
			<groupId>mysqlgroupId>
			<artifactId>mysql-connector-javaartifactId>
		dependency>
		
		<dependency>
			<groupId>com.alibabagroupId>
			<artifactId>druidartifactId>
			<version>1.0.9version>
		dependency>
	dependencies>

application.properties

#连接数据库
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ssm?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
#指定连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

#开启正向工程
spring.jpa.hibernate.ddl-auto=update
#运行时,打印sql语句
spring.jpa.show-sql=true

启动类

@SpringBootApplication
public class AppForSpringBootJap {
	public static void main(String[] args) {
		SpringApplication.run(AppForSpringBootJap.class, args);
	}
}

一对多的关联关系

需求:角色与用户的一对多的关联关系。
角色:一方
用户:多方

实体类-角色

注意编写一对多关系时,“一” 的实体类的toString()写法

@Entity
@Table(name="tb_roles")
public class Roles {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="role_id")
	private Integer roleid;
	@Column(name="role_name")
	private String  rolename;
	
	@OneToMany(mappedBy="roles")
	private Set<Users> users=new HashSet<Users>();
	
	//取值赋值方法,构造方法略

	//注意整理不能有Users对象,会出异常
	@Override
	public String toString() {
		return "Roles [roleid=" + roleid + ", rolename=" + rolename +"]";
	}

	

	
}

实体类-用户

@Entity
@Table(name="tb_users")
public class Users {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="id")
	private Integer id;
	@Column(name="name")
	private String name;
	@Column(name="age")
	private Integer age;
	@Column(name="address")
	private String address;
	
	@ManyToOne(cascade=CascadeType.PERSIST,fetch=FetchType.EAGER)
	@JoinColumn(name="role_id")
	private Roles roles;
	
    //取值赋值方法,构造方法略

}

接口类

public interface UserRepository extends JpaRepository<Users, Integer>{

}

测试代码

注意相关的注解!!!

/**
 * 测试一对多关系
 * 
 * @author chy
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes={AppForSpringBootJap.class})
public class TestOneToMany {

	@Autowired
	private UserRepository userDao;

	/**
	 * 添加用户同时添加角色
	 */
	@Test
	public void test1() {
		// 创建角色
		Roles roles = new Roles();
		roles.setRolename("管理员");
		// 创建用户
		Users users = new Users();
		users.setName("小辣椒");
		users.setAge(22);
		users.setAddress("纽约");
		// 建立关系
		roles.getUsers().add(users); // 为users这个set集合添加属性
		users.setRoles(roles); // 为user保存roles相关信息 ,如外键roles_id
		// 保存数据
		this.userDao.save(users);
	}

	/**
	 * 根据用户ID 查询用户信息,同时查询角色
	 */
	@Test
	public void test2() {
		Users users = this.userDao.findOne(2);
		System.out.println("用户姓名:" + users.getName());
		Roles roles = users.getRoles();
		System.out.println(roles);
	}

}


多对多关系

需求:角色与菜单多对多关联关系
角色:多方
菜单:多方

实体类-角色

@Entity
@Table(name="tb_roles")
public class Roles {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="role_id")
	private Integer roleid;
	@Column(name="role_name")
	private String  rolename;
	
	@OneToMany(mappedBy="roles")
	private Set<Users> users=new HashSet<Users>();
	
	@ManyToMany(cascade=CascadeType.PERSIST,fetch=FetchType.EAGER)
	@JoinTable(name="tb_roles_menus",joinColumns=@JoinColumn(name="role_id"),inverseJoinColumns=@JoinColumn(name="menu_id"))
	private Set<Menus>meuns=new HashSet<Menus>();
	


	//注意整理不能有Users对象,会出异常
	@Override
	public String toString() {
		return "Roles [roleid=" + roleid + ", rolename=" + rolename +"]";
	}

	//取值赋值,构造方法略

	
}

实体类-菜单

注意 toString()方法的修改!!!

@Entity
@Table(name="tb_menus")
public class Menus {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="menuId")
	private Integer menuId;
	@Column(name="menuName")
	private String menuName;
	@Column(name="menuUrl")
	private String menuUrl;
	@Column(name="fatherId")
	private Integer fatherId;
	
	@ManyToMany(mappedBy="menus")
	private Set<Roles> roles=new HashSet<Roles>();
	
	
	//不打印roles的信息,因为查不到(没有session)
	@Override
	public String toString() {
		return "Menus [menuId=" + menuId + ", menuName=" + menuName + ", menuUrl=" + menuUrl + ", fatherId=" + fatherId
				+ "]";
	}

	//取值赋值,构造方法略
	
}

接口类

public interface RolesRepository extends JpaRepository<Roles, Integer>{

}

测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes= {AppForSpringBootJap.class})
public class TestManyToMany {
	
	@Autowired
	private RolesRepository roleDao;
	
	/**
	* 添加角色同时添加菜单
	*/
	@Test
	public void test1() {
		// 创建角色
		Roles roles = new Roles();
		roles.setRolename("超管");;
		// 创建菜单
		Menus menus = new Menus();
		menus.setFatherId(-1);
		menus.setMenuName("CSND博客平台");
		menus.setMenuUrl("www.csdn.com");

		Menus menus2 = new Menus();
		menus2.setFatherId(1);
		menus2.setMenuName("博客园博客平台");
		menus2.setMenuUrl("www.cnblogs.com");

		// 添加关系
		roles.getMeuns().add(menus);
		roles.getMeuns().add(menus2);

		menus.getRoles().add(roles);
		menus2.getRoles().add(roles);
		// 保存数据
		this.roleDao.save(roles);
	}

	/**
	 * 查询Roles
	 */
	@Test
	public void test2() {
		Roles roles = this.roleDao.findOne(4);
		System.out.println("角色信息:" + roles);
		Set<Menus> menus = roles.getMeuns();
		for (Menus menus2 : menus) {
			System.out.println("菜单信息:" + menus2);
		}
	}
}

六、Spring Boot 整合Ehcache

通过 Ehcache实现对数据本地的缓存 , 用于单体架构的项目中

整合步骤

修改pom文件

	<properties>
		<thymeleaf.version>3.0.2.RELEASEthymeleaf.version>
		<thymeleaf-layout-dialect.version>2.0.4thymeleaf-layout-dialect.version>
	properties>

	
	<dependencies>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-thymeleafartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-data-jpaartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
		dependency>

		
		
		<dependency>
			<groupId>mysqlgroupId>
			<artifactId>mysql-connector-javaartifactId>
		dependency>
		
		<dependency>
			<groupId>com.alibabagroupId>
			<artifactId>druidartifactId>
			<version>1.0.9version>
		dependency>


		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-cacheartifactId>
		dependency>
		
		<dependency>
			<groupId>net.sf.ehcachegroupId>
			<artifactId>ehcacheartifactId>
		dependency>
	dependencies>

创建Ehcache 的配置文件

文件名:ehcache.xml
位置:src/main/resources/ehcache.xml


<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
	updateCheck="false">    
	    
	 
	<diskStore path="java.io.tmpdir" />


	<defaultCache 
		maxElementsInMemory="10000" 
		eternal="false"
		timeToIdleSeconds="120" 
		timeToLiveSeconds="120"
		maxElementsOnDisk="10000000" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
		<persistence strategy="localTempSwap" />
	defaultCache>
 
	<cache  name="users" 
		maxElementsInMemory="10000" 
		eternal="false"
		timeToIdleSeconds="120" 
		timeToLiveSeconds="120"
		maxElementsOnDisk="10000000" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
		<persistence strategy="localTempSwap" />
	cache>
ehcache>

修改application.properties 文件

#连接数据库
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ssm?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
#指定连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

#开启正向工程
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.generate-ddl=true
#运行时,打印sql语句
spring.jpa.show-sql=true

#使用缓存时需要在启动类上添加@EnableCaching注解  在需要开启注解的方法上添加@Cacheable
#开启ehcache的缓存配置
spring.cache.ehcache.config=ehcache.xml

修改启动类

加上@EnableCaching注解表示开启本地缓存

@SpringBootApplication
@EnableCaching
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

功能测试

dao层

public interface UserRepository extends JpaRepository<Users, Integer>{

}

业务层

public interface UsersService {
	void save(Users users);
	List<Users>findALl();
	Users findUserById(Integer id);
	Page<Users>findUserByPage(Pageable pageable);
	
	
}

@CacheEvict(value=“users”,allEntries=true) 清除缓存中以users 存策略缓存的对象
@Cacheable(“users”) 这个注解是选择缓存的类型,需要在启动类开启@EnableCaching

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import ah.szxy.dao.UserRepository;
import ah.szxy.pojo.Users;

@Service
public class UsersServiceImpl implements UsersService{
	@Autowired
	private UserRepository userRepository;
	
	

	@Override
	public List<Users> findALl() {

		return this.userRepository.findAll();
	}

	/**
	 * 没有方法参数时,会默认以0为key,当有两个无参方法使用缓存时mkey会冲突。可以使用数字作为key,不能直接使用字符串
	 */
	@Override
	//@Cacheable:对当前查询的对象做缓存处理
	@Cacheable("users")  //这个注解是选择缓存的类型,需要在启动类开启@EnableCaching
	public Users findUserById(Integer id) {

		return this.userRepository.findOne(id);
	}
	
	
	@Override
	@Cacheable(value="users",key="#pageable.pageSize")
	public Page<Users> findUserByPage(Pageable pageable) {

		return this.userRepository.findAll(pageable);
	}
	
	//@CacheEvict(value="users",allEntries=true) 清除缓存中以users 存策略缓存的对象
	@Override  //实现了在在添加用户时清除本地缓存 ,防止数据的脏读
	@CacheEvict(value="users",allEntries=true)
	public void save(Users users) {
		this.userRepository.save(users);
	}
	
	
}

测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes=App.class)
public class UsersServiceTest {
	@Autowired
	private UsersService usersService;
	
	@Test
	public void testfindUserById() {
		Users user = usersService.findUserById(1);
		System.out.println(user);
		Users user2 = usersService.findUserById(1);
		System.out.println(user2);
//		//第一次查询
//		System.out.println(this.usersService.findUserById(1));
//		//第二次查询
//		System.out.println(this.usersService.findUserById(1));
		
		
	}
	
	@Test
	public void TestPageable() {
		//第一次
		Pageable pageable=new PageRequest(0, 2);
		Page<Users> page = this.usersService.findUserByPage(pageable);
		System.out.println("总条数"+page.getTotalElements());
		//第二次
		Page<Users> page2 = this.usersService.findUserByPage(pageable);
		System.out.println("总条数"+page2.getTotalElements());
		//第三次 ,在实现类@Cacheable(value="users",key="#pageable.pageSize")
		//表示以page的size作为key ,只要key一样,就会默认使用本地缓存而不会执行对数据库的查询
		Pageable pageable2=new PageRequest(1, 2);
		Page<Users> page3 = this.usersService.findUserByPage(pageable2);
		System.out.println("总条数"+page3.getTotalElements());
		
		
	}
	
	@Test
	public void test3() {
		Users user = usersService.findUserById(1);
		System.out.println(user);
		
		Users users = new Users();
		users.setName("chy");
		users.setAge(24);
		users.setAddress("szxt");
		this.usersService.save(users);
		
		Users user2 = usersService.findUserById(1);
		System.out.println(user2);
	
		
	}
	
	
}

常用注解解析

点击查询常用注解

七、整合Redis

创建maven项目

导入maven依赖

启动器的父类

<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>1.5.10.RELEASEversion>
	parent>

其他依赖支持

	
	<properties>
		<thymeleaf.version>3.0.2.RELEASEthymeleaf.version>
		<thymeleaf-layout-dialect.version>2.0.4thymeleaf-layout-dialect.version>
	properties>

	<dependencies>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-thymeleafartifactId>
		dependency>

		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-data-redisartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
		dependency>
	dependencies>

redis参数配置文件application.properties

文件名:application.properties
位置:src/main/resources/application.properties

spring.redis.pool.max-idle=10
spring.redis.pool.min-idle=5
spring.redis.pool.max-total=20

spring.redis.hostName=192.168.179.131
spring.redis.port=6379

redis的配置类

/**
 * 完成对Redis的整合的一些配置
 * 
 * @author chy
 *
 */
@Configuration
public class RedisConfig {

	/**
	* 1.创建JedisPoolConfig 对象。在该对象中完成一些链接池配置
	* @ConfigurationProperties:会将前缀相同的内容创建一个实体。
	*/
	@Bean
	@ConfigurationProperties(prefix="spring.redis.pool")
	public JedisPoolConfig jedisPoolConfig(){
		JedisPoolConfig config = new JedisPoolConfig();
		//最大空闲数
		//config.setMaxIdle(10);
		//最小空闲数
		//config.setMinIdle(5);
		//最大链接数
		//config.setMaxTotal(20);
		System.out.println("默认值:"+config.getMaxIdle());
		System.out.println("默认值:"+config.getMinIdle());
		System.out.println("默认值:"+config.getMaxTotal());
		return config;
	}
	
	/**
	 * 2.创建JedisConnectionFactory:配置redis链接信息
	 */
	@Bean
	@ConfigurationProperties(prefix="spring.redis")
	public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig config){
		System.out.println("配置完毕:"+config.getMaxIdle());
		System.out.println("配置完毕:"+config.getMinIdle());
		System.out.println("配置完毕:"+config.getMaxTotal());
		
		
		JedisConnectionFactory factory = new JedisConnectionFactory();
		//关联链接池的配置对象
		factory.setPoolConfig(config);
		//配置链接Redis的信息
		//主机地址
		//factory.setHostName("192.168.179.131");
		//端口
		//factory.setPort(6379);
		
		return factory;
	}
	
	/**
	 * 3.创建RedisTemplate:用于执行Redis操作的方法
	 */
	@Bean
	public RedisTemplate<String,Object> redisTemplate(JedisConnectionFactory factory){
		RedisTemplate<String, Object> template = new RedisTemplate<>();
		//关联
		template.setConnectionFactory(factory);
		
		//为key设置序列化器
		template.setKeySerializer(new StringRedisSerializer());
		//为value设置序列化器
		template.setValueSerializer(new StringRedisSerializer());
		return template;
	}
}

实体类

public class Users implements Serializable{
	
	private Integer id;
	private String name;
	private Integer age;
	private String address;
//洽谈方法省略
}

启动类

/**
 * SpringBoot整合Mybatis
 * @author chy
 *
 */
@SpringBootApplication
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes=App.class)
public class TestRedis {
	
	@Autowired
	private RedisTemplate<String, Object> redisTemplate;
	
	/**
	 * 插入一个字符串
	 */
	@Test
	public void TestSet() {
		
		this.redisTemplate.opsForValue().set("name", "乔鲁诺乔斯达");
	}
	
	/**
	 * 从redis中取值
	 */
	@Test
	public void TestGet() {
		
		String name = (String) this.redisTemplate.opsForValue().get("name");
		System.out.println(name);
	}
	
/**
	 * 设置Users对象
	 *注意通过: JdkSerializationRedisSerializer序列化器所占内存较大
	 */
	@Test
	public void TestObjectSet() {
		
		Users users = new Users();
		users.setId(1);
		users.setName("乔鲁诺乔巴纳");
		users.setAge(16);
		users.setAddress("意大利罗马");
		this.redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
		this.redisTemplate.opsForValue().set("Users", users);
		
	}
	
	/**
	 * 获取Users对象
	 *注意通过: JdkSerializationRedisSerializer序列化器所占内存较大
	 */
	@Test
	public void TestObjectGet() {
		
		this.redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
		Users users = (Users) this.redisTemplate.opsForValue().get("Users");
		System.out.println(users);
	}
	


	/**
	 * 设置Users对象,通过Json序列化器
	 *占用内存较小
	 */
	@Test
	public void TestObjectSetJson() {
		
		Users users = new Users();
		users.setId(1);
		users.setName("乔鲁诺乔巴纳");
		users.setAge(16);
		users.setAddress("意大利罗马");
		this.redisTemplate.setValueSerializer(new  Jackson2JsonRedisSerializer<>(Users.class));
		this.redisTemplate.opsForValue().set("Users_Json", users);
		
	}
	
	
	/**
	 * 获取Users对象,通过Json序列化器
	 *占用内存较小
	 */
	@Test
	public void TestObjectGetJson() {
		
		this.redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Users.class));
		Users users = (Users) this.redisTemplate.opsForValue().get("Users_Json");
		System.out.println(users);
	}
}

常用的序列化器
Spring全家桶之SpringBoot——高级阶段_第12张图片

八、 定时任务

1. Scheduled

项目框架搭建

引入maven依赖


<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
dependency>

创建定时器

//不属于控制层、服务层、数据访问层。所以使用@Component
@Component
public class ScheduledDemo {
	/**
	 * 每两秒打印一次时间
	 * cron="0/2 * * * * *"
	 * 
	 * 每分钟的第二秒打印一次时间
	 * cron="2 * * * * *"
	 */
	@Scheduled(cron="0/2 * * * * ?")
	public void doScheduled() {
		System.out.println("定时器:"+new Date());
		
	}
}

启动器(注意:使用了@EnableScheduling注解):

/**
 * SpringBoot整合定时器任务
 * 在开启启动类时,需要额外加@EnableScheduling注解
 * 
 * @author chy
 *
 */
@SpringBootApplication
@EnableScheduling
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

cron 表达式讲解

Cron 表达式是一个字符串,分为6 或7 个域,每一个域代表一个含义
Cron 有如下两种语法格式:
(1) Seconds Minutes Hours Day Month Week Year
(2) Seconds Minutes Hours Day Month Week(推荐使用)

一、结构
corn 从左到右(用空格隔开):秒分小时月份中的日期月份星期中的日期年份
二、各字段的含义

Spring全家桶之SpringBoot——高级阶段_第13张图片

Cron 表达式的时间字段除允许设置数值外,还可使用一些特殊的字符,提供列表、范围、通配符等功能,细说如下:

特殊字符名称 作用
星号(*) 可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”;
问号(?) 该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于占位符;
减号(-) 表达一个范围,如在小时字段中使用“10-12”,则表示从10 到12 点,即10,11,12;
逗号(,): 表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
斜杠(/) x/y 表达一个等步长序列,x 为起始值,y 为增量步长值。如在分钟字段中使用0/15,
则表示为0,15,30 和45 秒,而5/15 在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
LW 组合 在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;
井号(#) 该字符只能在星期字段中使用,表示当月某个工作日。如6#3 表示当月的第三个星期五(6表示星期五,#3 表示当前的第三个),而4#5 表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
C 该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C 在日期字段中就相当于日历5 日以后的第一天。
1C 在星期字段中相当于星期日后的第一天。
L 该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。
L 在日期字段中,表示这个月份的最后一天,如一月的31 号,非闰年二月的28 号;
如果L 用在星期中,则表示星期六,等同于7。但是,如果L 出现在星期字段里,而且在前面有一个数值X,
则表示“这个月的最后X 天”,例如,6L 表示该月的最后星期五;
W 该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。
例如15W表示离该月15 号最近的工作日,如果该月15 号是星期六,则匹配14 号星期五;如果15 日是星期日,则匹配16 号星期一;
如果15 号是星期二,那结果就是15 号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1 号是星期六,结果匹配的是3 号星期一,而非上个月最后的那天。W 字符串只能指定单一日期,而不能指定日期范围;

注 : Cron 表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。

例子 作用
@Scheduled(cron = “0 0 1 1 1 ?”) 每年一月的一号的1:00:00 执行一次
@Scheduled(cron = “0 0 1 1 1,6 ?”) 一月和六月的一号的1:00:00 执行一次
@Scheduled(cron = “0 0 1 1 1,4,7,10 ?”) 每个季度的第一个月的一号的1:00:00 执行一次
@Scheduled(cron = “0 0 1 1 * ?”) 每月一号1:00:00 执行一次
@Scheduled(cron=“0 0 1 * * *”) 每天凌晨1 点执行一次

2. Quartz定时任务框架

Quartz介绍

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.3.0。

项目框架搭建

创建项目(简单的javase的maven项目)
导入依赖:


<dependency>
	<groupId>org.quartz-schedulergroupId>
	<artifactId>quartzartifactId>
	<version>2.2.1version>
dependency>

创建定时任务:

//需要实现job接口
public class QuartzJob implements Job{

	@Override
	public void execute(JobExecutionContext arg0) throws JobExecutionException {
		System.out.println("定时任务:"+new Date());
	}
}

启动定时任务:

public class QuartzStart {
	
	public static void main(String[] args) throws Exception {
		
		//创建定时任务 job  做什么
		JobDetail job = JobBuilder.newJob(QuartzJob.class).build();
		
		//创建定时器 trigger  什么时候做
		//两种定时方式
		//SimpleScheduleBuilder简单定时:通过Quartz 提供一个方法来完成简单的重复调用
		//CronScheduleBuilder 表达式定时:cron表达式
		//Trigger trigger=TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(2)).build();
		Trigger trigger=TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")).build();
		
		//定时任务与定时器绑定Scheduler  在什么时候做什么
		Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
		scheduler.scheduleJob(job, trigger);
		
		//启动
		scheduler.start();
	}
}

整合SpringBoot

创建项目
导入依赖:


	<properties>
		<thymeleaf.version>3.0.2.RELEASEthymeleaf.version>
		<thymeleaf-layout-dialect.version>2.0.4thymeleaf-layout-dialect.version>
	properties>

	<dependencies>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-thymeleafartifactId>
		dependency>

		
		<dependency>
			<groupId>org.springframeworkgroupId>
			<artifactId>spring-context-supportartifactId>
		dependency>

		
		<dependency>
			<groupId>org.quartz-schedulergroupId>
			<artifactId>quartzartifactId>
			<version>2.2.1version>
			<exclusions>
				<exclusion>
					<artifactId>slf4j-apiartifactId>
					<groupId>org.slf4jgroupId>
				exclusion>
			exclusions>
		dependency>
		
		<dependency>
			<groupId>org.springframeworkgroupId>
			<artifactId>spring-txartifactId>
		dependency>

	dependencies>

定时任务:

public class JobDemo implements Job{

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		System.out.println("定时任务:"+new Date());
	}
}

配置类:

/**
 * Quartz配置类
 * @author Administrator
 *
 */
@Configuration
public class QuartzConfig {

	@Bean
	public JobDetailFactoryBean  jobDetailFactoryBean() {
		JobDetailFactoryBean factoryBean=new JobDetailFactoryBean();
		factoryBean.setJobClass(JobDemo.class);
		return factoryBean;
	}
	
//	@Bean
//	public SimpleTriggerFactoryBean simpleTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean) {
//		SimpleTriggerFactoryBean factoryBean=new SimpleTriggerFactoryBean();
//		factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
//		//间隔时间
//		factoryBean.setRepeatInterval(2000);//ms
//		//执行次数
//		factoryBean.setRepeatCount(4);
//		return factoryBean;
//	}
	@Bean
	public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean) {
		CronTriggerFactoryBean factoryBean=new CronTriggerFactoryBean();
		factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
		factoryBean.setCronExpression("0/2 * * * * ?");
		return factoryBean;
	}
	
//	@Bean
//	public  SchedulerFactoryBean schedulerFactoryBean(SimpleTriggerFactoryBean simpleTriggerFactoryBean) {
//		
//		SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
//		factoryBean.setTriggers(simpleTriggerFactoryBean.getObject());
//		return factoryBean;
//	}
	@Bean
	public  SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean cronTriggerFactoryBean) {
		
		SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
		factoryBean.setTriggers(cronTriggerFactoryBean.getObject());
		return factoryBean;
	}
}

启动类:

@SpringBootApplication
@EnableScheduling//开启定时器
public class StartApp {

	public static void main(String[] args) {
		SpringApplication.run(StartApp.class, args);
	}
}

问题:在定时任务中无法调用其他对象,spring无法注入对象。
原因:在quartz中定时任务的创建时通过AdaptableJobFactory类中的createJobInstance方法创建,使用的是反射,没有使用spring,所以无法注入对象。
解决:复写createJobInstance方法,将创建的定时任务加入到spring容器中。

package ah.szxy.config;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;

@Component("myAdaptableJobFactory")
public class MyAdaptableJobFactory extends AdaptableJobFactory {

	// AutowireCapableBeanFactory 可以将一个对象添加到SpringIOC 容器中,并且完成该对象注入
	@Autowired
	private AutowireCapableBeanFactory autowireCapableBeanFactory;

	/**
	 * 该方法需要将实例化的任务对象手动的添加到springIOC 容器中并且完成对 象的注入
	 */
	@Override
	protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
		Object obj = super.createJobInstance(bundle);
		// 将obj 对象添加Spring IOC 容器中,并完成注入
		this.autowireCapableBeanFactory.autowireBean(obj);
		return obj;
	}
}

在配置类中设置

	/**
	 * 3.创建Scheduler对象
	 */
	@Bean
	public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean cronTriggerFactoryBean,
			MyAdaptableJobFactory myAdaptableJobFactory){
		SchedulerFactoryBean factory = new SchedulerFactoryBean();
		//关联trigger
		factory.setTriggers(cronTriggerFactoryBean.getObject());
		factory.setJobFactory(myAdaptableJobFactory);
		return factory;
	}

在实际开发中关于定时器的应用:

https://blog.csdn.net/weixin_39723544/article/details/83382000
https://www.cnblogs.com/ealenxie/p/9134602.html

你可能感兴趣的:(JAVA小窝(笔记),Spring技术)