前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring全家桶之SpringBoot——高级阶段

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

作者头像
时间静止不是简史
发布2020-07-27 10:43:23
3.3K0
发布2020-07-27 10:43:23
举报
文章被收录于专栏:Java探索之路Java探索之路

介绍

前提

• 学习过SpringData Jpa课程

• 学习过SpringData Redis课程

• 学习过 SpringBoot 初级阶段

简介

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

学习导图

一、服务端表单数据校验

需求

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

环境搭建

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

主要代码

视图层

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

代码语言:javascript
复制
<font color="red" th:errors="${users.name}"></font>

add.html

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- th:errors 会湖区响应的数据 .有,会将数据取出,没有会报异常 -->
	<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"; }

代码语言:javascript
复制
@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 对象修改别名 ,使用后,前端获取方式应该同样改变,类似下面这样

代码语言:javascript
复制
  用户姓名:<input type="text" name="name" />
    <font color="red" th:errors="${aa.name}"></font><br /> 
代码语言:javascript
复制
@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";
	}
}

实体类

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

代码语言:javascript
复制
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 请求,然后跳转到默认显示异常的页面来展示异常信息。

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

error.html

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
错误了...请自我检讨~~~
</body>
</html>

Controller层

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

@ExceptionHandle 注解处理异常

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

error1.html

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>错误提示页面-ArithmeticException</title>
</head>
<body>
	出错了,请与管理员联系。。。
	<span th:text="${error}"></span>
</body>
</html>

error2.html

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>错误提示页面-NullPointerException</title>
</head>
<body>
	出错了,请与管理员联系。。。
	<span th:text="${error}"></span>
</body>
</html>

controller

代码语言:javascript
复制
@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 注解处理异常

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

代码语言:javascript
复制
/**
 * 全局异常处理类
 * 使用@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 处理异常

代码语言:javascript
复制
/**
 * 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 类处理异常

代码语言:javascript
复制
/**
 * 自定义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启动器之后 ,需要额外添加用于支持一个单元测试的启动器

代码语言:javascript
复制
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
  </parent>

	<!-- springBoot 的启动器_全栈式开发 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- springBoot 的启动器_单元测试 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
		</dependency>

测试类

  1. 在测试类头部需要加上 ,下面两个注解 ,并创建启动类 @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = { AppForJunit.class })
  2. 在被测试的方法上加上@Test注解
代码语言:javascript
复制
/**
 * 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();
	}
}

启动类

代码语言:javascript
复制
@SpringBootApplication
public class AppForJunit {
	
	public static void main(String[] args) {
		SpringApplication.run(AppForJunit.class, args);
	}
}

测试结果

四、SpringBoot 热部署

1. SpringLoader 插件的使用

复制一个SpringBoot的web项目

修改pom文件

添加SpringLoader 插件

代码语言:javascript
复制
<!-- springloader 插件 -->
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<dependencies>
					<dependency>
						<groupId>org.springframework</groupId>
						<artifactId>springloaded</artifactId>
						<version>1.2.5.RELEASE</version>
					</dependency>
				</dependencies>
			</plugin>
		</plugins>
	</build>

使用maven 的命令起来启动

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

代码语言:javascript
复制
spring-boot:run
SpringLoader 缺陷
  1. 就是Java 代码做部署处理。但是对页面无能为力。
  2. 这种方式的缺点是Springloader 热部署程序是在系统后台以进程的形式来运行。需要手动关闭该进程
补充 : 如何使用命令行杀死进程
  1. 输入 tasklist ,会显示正在运行的进程以及他们的pid ,找到对应的pid
  1. 找到需要结束进程的pid编号,输入taskkill /pid 编号 /f,按回车键,如下图所示:
  1. 也可以通过进程的名称来结束进程,输入taskkill /im 进程名称(带上后缀) /f 就可以关闭进程了,

使用jar 包的方式

添加springloader 的jar 包
启动方式
代码语言:javascript
复制
启动命令:
-javaagent:.\lib\springloaded-1.2.5.RELEASE.jar -noverify

2. DevTools 工具的使用

SpringLoader 与DevTools 的区别

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

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

使用方式

修改项目的pom 文件添加devtools 的依赖
代码语言:javascript
复制
<!-- DevTools 的坐标 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
		</dependency>
运行启动类即可

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

五、Spring Boot 整合Spring Data JPA

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的坐标
代码语言:javascript
复制
<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.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
	</parent>
	<groupId>ah.szxy.SpringBoot</groupId>
	<artifactId>22-SpringBoot-Jpa</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	
<!-- 部署tomcat,使用thymeleaf进行开发,采取Jpa规范和单元测试-->
	<dependencies>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<!-- 单元测试的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
		</dependency>

<!-- 使用数据库连接的jar一般与连接池一起使用 -->
		<!-- mysql -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!-- druid 连接池 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.0.9</version>
		</dependency>
	</dependencies>

</project>

在项目中添加application.properties 文件

代码语言:javascript
复制
#连接数据库
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

添加实体类

注意相关注解的使用

代码语言:javascript
复制
@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 接口

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

}

创建启动类

代码语言:javascript
复制
@SpringBootApplication
public class AppForSpringBootJap {
	public static void main(String[] args) {
		SpringApplication.run(AppForSpringBootJap.class, args);
	}
}

测试类代码

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

代码语言:javascript
复制
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;

所注入的接口

代码语言:javascript
复制
@Autowired
	private UsersRepository userDao;
	@Autowired
	private UsersRepositoryByName usersRepositoryByName;
	@Autowired
	private UsersRepositoryQueryAnnotation usersRepositoryQueryAnnotation;
	@Autowired
	private UsersRepositoryCrudRepository usersRepositoryCrudRepository;
	@Autowired
	private UsersRepositoryPagingAndSorting usersRepositoryPagingAndSorting;

测试代码

代码语言:javascript
复制
@Test
	public void addUser() {
		Users users = new Users();
		users.setName("王晓红");
		users.setAge(20);
		users.setAddress("北京大兴区");
		this.userDao.save(users);
	}

Spring Data JPA 提供的核心接口

1. Repository 接口

接口类
代码语言:javascript
复制
	//方法的名称必须要遵循驼峰式命名规则。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);
}
方法名称命名查询方式
测试代码
代码语言:javascript
复制
@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. 更新需要开启事务 ,关闭回滚
测试代码
代码语言:javascript
复制
@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 接口, 所能调用的方法如下

接口类
代码语言:javascript
复制
public interface UsersRepositoryCrudRepository extends CrudRepository<Users, Integer>{

}
测试代码
代码语言:javascript
复制
/**
	 * 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 接口

接口类
代码语言:javascript
复制
public interface UsersRepositoryPagingAndSorting extends PagingAndSortingRepository<Users, Integer> {

}
测试代码
代码语言:javascript
复制
	/**
	 * 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 ,在进行查询时无需对返回结果进行强转 ,方便我们编程

接口类
代码语言:javascript
复制
/**
* 参数一T :当前需要映射的实体
* 参数二ID :当前映射的实体中的OID 的类型
*
*/
public interface UsersRepository extends JpaRepository<Users, Integer>{

}
测试代码
代码语言:javascript
复制
/**
	 * 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文件

代码语言:javascript
复制
<!-- 部署tomcat,使用thymeleaf进行开发,采取Jpa规范和单元测试-->
	<dependencies>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<!-- 单元测试的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
		</dependency>

<!-- 使用数据库连接的jar一般与连接池一起使用 -->
		<!-- mysql -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!-- druid 连接池 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.0.9</version>
		</dependency>
	</dependencies>

application.properties

代码语言:javascript
复制
#连接数据库
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

启动类

代码语言:javascript
复制
@SpringBootApplication
public class AppForSpringBootJap {
	public static void main(String[] args) {
		SpringApplication.run(AppForSpringBootJap.class, args);
	}
}

一对多的关联关系

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

角色:一方

用户:多方

实体类-角色

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

代码语言:javascript
复制
@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 +"]";
	}

	

	
}
实体类-用户
代码语言:javascript
复制
@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;
	
    //取值赋值方法,构造方法略

}
接口类
代码语言:javascript
复制
public interface UserRepository extends JpaRepository<Users, Integer>{

}
测试代码

注意相关的注解!!!

代码语言:javascript
复制
/**
 * 测试一对多关系
 * 
 * @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);
	}

}

多对多关系

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

角色:多方

菜单:多方

实体类-角色
代码语言:javascript
复制
@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()方法的修改!!!

代码语言:javascript
复制
@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
				+ "]";
	}

	//取值赋值,构造方法略
	
}
接口类
代码语言:javascript
复制
public interface RolesRepository extends JpaRepository<Roles, Integer>{

}
测试代码
代码语言:javascript
复制
@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文件

代码语言:javascript
复制
	<properties>
		<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
		<thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version>
	</properties>

	<!-- 部署tomcat,使用thymeleaf进行开发,采取Jpa规范和单元测试 -->
	<dependencies>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<!-- 单元测试的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
		</dependency>

		<!-- 使用数据库连接的jar一般与连接池一起使用 -->
		<!-- mysql -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!-- druid 连接池 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.0.9</version>
		</dependency>


		<!-- Spring Boot 缓存支持启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
		<!-- Ehcache 坐标 -->
		<dependency>
			<groupId>net.sf.ehcache</groupId>
			<artifactId>ehcache</artifactId>
		</dependency>
	</dependencies>

创建Ehcache 的配置文件

文件名:ehcache.xml

位置:src/main/resources/ehcache.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
	updateCheck="false">    
	<!-- EhCache在每次启动的时候都要连接到 ehcache 网站上去检查新版本 使用如上的 updateCheck="false" 来禁止这个检查新版本 -->    
	 <!-- 
	name:cache唯一标识
	eternal:缓存是否永久有效 
	maxElementsInMemory:内存中最大缓存对象数 
	overflowToDisk(true,false):缓存对象达到最大数后,将缓存写到硬盘中 
	diskPersistent:硬盘持久化 
	timeToIdleSeconds:缓存清除时间 
	timeToLiveSeconds:缓存存活时间 
	diskExpiryThreadIntervalSeconds:磁盘缓存的清理线程运行间隔 
	memoryStoreEvictionPolicy:缓存清空策略 
		1.FIFO:first in first out 先讲先出 
		2.LFU: Less Frequently Used 一直以来最少被使用的 
		3.LRU:Least Recently Used 最近最少使用的 
		-->
	<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 文件

代码语言:javascript
复制
#连接数据库
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注解表示开启本地缓存

代码语言:javascript
复制
@SpringBootApplication
@EnableCaching
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

功能测试

dao层

代码语言:javascript
复制
public interface UserRepository extends JpaRepository<Users, Integer>{

}

业务层

代码语言:javascript
复制
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

代码语言:javascript
复制
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);
	}
	
	
}

测试代码

代码语言:javascript
复制
@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依赖

启动器的父类

代码语言:javascript
复制
<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
	</parent>

其他依赖支持

代码语言:javascript
复制
	<!-- 对thymeleaf进行优化,使其对html文件的格式不做严格要求 , 降低开发难度 -->
	<properties>
		<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
		<thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version>
	</properties>

	<dependencies>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<!-- Spring Data Redis 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<!-- 单元测试启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
		</dependency>
	</dependencies>

redis参数配置文件application.properties

文件名:application.properties

位置:src/main/resources/application.properties

代码语言:javascript
复制
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的配置类

代码语言:javascript
复制
/**
 * 完成对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;
	}
}

实体类

代码语言:javascript
复制
public class Users implements Serializable{
	
	private Integer id;
	private String name;
	private Integer age;
	private String address;
//洽谈方法省略
}

启动类

代码语言:javascript
复制
/**
 * SpringBoot整合Mybatis
 * @author chy
 *
 */
@SpringBootApplication
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

测试类

代码语言:javascript
复制
@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);
	}
}

常用的序列化器

八、 定时任务

1. Scheduled

项目框架搭建

引入maven依赖

代码语言:javascript
复制
<!-- 添加Scheduled 坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>

创建定时器

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

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

代码语言:javascript
复制
/**
 * 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 从左到右(用空格隔开):秒分小时月份中的日期月份星期中的日期年份

二、各字段的含义

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项目)

导入依赖:

代码语言:javascript
复制
<!-- Quartz 坐标 -->
<dependency>
	<groupId>org.quartz-scheduler</groupId>
	<artifactId>quartz</artifactId>
	<version>2.2.1</version>
</dependency>

创建定时任务:

代码语言:javascript
复制
//需要实现job接口
public class QuartzJob implements Job{

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

启动定时任务:

代码语言:javascript
复制
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

创建项目

导入依赖:

代码语言:javascript
复制
<!-- 对thymeleaf进行优化,使其对html文件的格式不做严格要求 , 降低开发难度 -->
	<properties>
		<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
		<thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version>
	</properties>

	<dependencies>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- springBoot 的启动器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<!-- 添加Scheduled 坐标 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
		</dependency>

		<!-- Quartz 坐标 -->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.2.1</version>
			<exclusions>
				<exclusion>
					<artifactId>slf4j-api</artifactId>
					<groupId>org.slf4j</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- Sprng tx 坐标 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
		</dependency>

	</dependencies>

定时任务:

代码语言:javascript
复制
public class JobDemo implements Job{

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

配置类:

代码语言:javascript
复制
/**
 * 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;
	}
}

启动类:

代码语言:javascript
复制
@SpringBootApplication
@EnableScheduling//开启定时器
public class StartApp {

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

问题:在定时任务中无法调用其他对象,spring无法注入对象。

原因:在quartz中定时任务的创建时通过AdaptableJobFactory类中的createJobInstance方法创建,使用的是反射,没有使用spring,所以无法注入对象。

解决:复写createJobInstance方法,将创建的定时任务加入到spring容器中。

代码语言:javascript
复制
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;
	}
}

在配置类中设置

代码语言:javascript
复制
	/**
	 * 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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-08-31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
    • 前提
      • 简介
        • 学习导图
        • 一、服务端表单数据校验
          • 需求
            • 环境搭建
              • 主要代码
                • 视图层
                • Controller层
                • 实体类
              • 表单校验常用注解总结
              • 二、SpringBoot中异常处理的方式
                • 自定义错误页面
                  • @ExceptionHandle 注解处理异常
                    • @ControllerAdvice + @ExceptionHandle 注解处理异常
                      • 配置SimpleMappingExceptionResolver 处理异常
                        • 自定义 HandlerExceptionResolver 类处理异常
                        • 三、Spring Boot 整合Junit 单元测试
                          • 步骤
                            • 创建jar 项目,修改pom文件
                            • 测试类
                            • 启动类
                        • 四、SpringBoot 热部署
                          • 1. SpringLoader 插件的使用
                            • 修改pom文件
                            • 使用maven 的命令起来启动
                            • 使用jar 包的方式
                          • 2. DevTools 工具的使用
                            • SpringLoader 与DevTools 的区别
                            • 使用方式
                        • 五、Spring Boot 整合Spring Data JPA
                          • Spring Data JPA 介绍
                            • Spring Boot 整合Spring Data JPA
                              • 创建项目 ,修改pom文件
                              • 在项目中添加application.properties 文件
                              • 添加实体类
                              • 编写Dao 接口
                              • 创建启动类
                              • 测试类代码
                              • Spring Data JPA 提供的核心接口
                              • 1. Repository 接口
                              • 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
                                        相关产品与服务
                                        云数据库 Redis
                                        腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                                        领券
                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档