日常SpringBoot缓存注解器及整合redis实现(附近期一些python零碎的内容)

Posted 囚生CY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常SpringBoot缓存注解器及整合redis实现(附近期一些python零碎的内容)相关的知识,希望对你有一定的参考价值。

序言

似乎灵感枯竭了,完全不知道该写些什么东西,非常的痛苦,决定走读博这条路之后就失去了很多乐趣,总是想应该积累更多的论文和相关项目代码量,但是效率却总是很低,需要应付课程与考试,一篇paper的阅读进度能拖到一周以上,如果还要理解代码就更让人绝望了。顺带提一句【论文阅读】正则表达式也可以被当成神经网络训练吗?附项目代码与代码详细说明 已经更新,11月14日论文作者已经将代码上传到paper中给到的GitHub链接,这份项目代码提供了完整的数据(可以从腾讯云下载且数据量比较小,很容易下载),且代码总量不是很大,笔者做了一份jupyter notebook测试并文字解析了代码各个模块的功能逻辑,一并更新在上文链接中给出的百度云链接中(附带了项目源码),个人认为是一篇非常适合不同水平学习者学习的代码,因为代码逻辑清晰,总量不大,适合入门学习;但其中包含了将正则表达式转为自动机,再转为三维张量,最后作张量秩分解后作为RNN参数,甚至对正则表达式进行倒置从而衍生出双向变体,这种思路非常清奇,很值得借鉴与拓展。

也许随着时间推移再难重整动力去写与paper无关的代码,因为做了就可能会有一种不务正业的负罪感,从这个角度来说这学期的高级软件工程可能是笔者最后一次可以系统性地去专门学与计算机有关的技术了,以后可能只会再确然需要时才会去学一些零碎的点。

本文是对【学习笔记】雷丰阳SpringBoot教程摘要 一文中SprintBoot缓存的详细说明与延申(有什么不理解的可以回头把雷神的教程再过一遍)结合实际案例操作结果来看看缓存的实际作用(因为实际测试缓存时发现),这将在第一部分描述;第二部分是关于WIN10下安装Redis后整合进SpringBoot的测试;第三部分是笔者将旧电脑配置为sftp服务器后出于稳定连接,连接安全等因素写了一些脚本,以及一些其他零碎,登不上大雅之堂,权当自娱。


目录


1 SpringBoot缓存注解器用法

1.1 相关模块代码准备

  1. 为了测试缓存我们先在项目的com.sufe.model包下写一个User.java的javabean类作为数据表,并在mysql数据库中新建一张User表与其对应:
  • User.java类:
    package com.sufe.model;
    
    public class User 
    	private int userId;
    	private String username;
    	private String password;
    	private String email;
    	private boolean isProfessional;
    	private boolean isAdministrator;
    	private String profileURL;
    	
    	public int getUserId() 
    		return userId;
    	
    	public void setUserId(int userId) 
    		this.userId = userId;
    	
    	public String getUsername() 
    		return username;
    	
    	public void setUsername(String username) 
    		this.username = username;
    	
    	public String getPassword() 
    		return password;
    	
    	public void setPassword(String password) 
    		this.password = password;
    	
    	public String getEmail() 
    		return email;
    	
    	public void setEmail(String email) 
    		this.email = email;
    	
    	public boolean isProfessional() 
    		return isProfessional;
    	
    	public void setProfessional(boolean isProfessional) 
    		this.isProfessional = isProfessional;
    	
    	public boolean isAdministrator() 
    		return isAdministrator;
    	
    	public void setAdministrator(boolean isAdministrator) 
    		this.isAdministrator = isAdministrator;
    	
    	public String getProfileURL() 
    		return profileURL;
    	
    	public void setProfileURL(String profileURL) 
    		this.profileURL = profileURL;
    	
    
    
    
  • User表建表语句:
    /* User表建表语句 */
    CREATE TABLE `User` (
    	userId INT(64) NOT NULL AUTO_INCREMENT,
    	username VARCHAR(32) NOT NULL,
    	password VARCHAR(32) NOT NULL,
    	email VARCHAR(256) NOT NULL,
    	isProfessional BIT NOT NULL,
    	isAdministrator BIT NOT NULL,
    	profileURL VARCHAR(256),
    	PRIMARY KEY (userId),
    	UNIQUE (username),
    	UNIQUE (email)
    );
    
    /* User插入语句示例 */
    INSERT INTO User(username,password,email,isProfessional,isAdministrator) VALUES("test001","123456","test001@test.com",1,1);
    INSERT INTO User(username,password,email,isProfessional,isAdministrator,profileURL) VALUES("test002","123456","test002@test.com",1,1,NULL);
    
  1. 然后在com.sufe.mapper包下编写UserMapper.java定义一些常用数据库操作的接口,并在对应的mybatis映射xml文件中写好sql语句,三种缓存注解器@Cacheable@CachePut@CacheEvict,就是在查询,删除和插入三种操作进行测试的:
  • UserMapper.java接口:
    package com.sufe.mapper;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Mapper;
    
    import com.alibaba.fastjson.JSONObject;
    import com.sufe.model.User;
    
    @Mapper
    public interface UserMapper 
    	
    	/*
    	 * 查询语句
    	 */
    	public List<User> getUserByUserId(int userId);
    	public List<User> getUserByUsername(String username);
    	public JSONObject test(String username);
    	
    	
    	/*
    	 * 删除语句
    	 */
    	public void deleteUserByUserId(int userId);
    	public void deleteUserByUsername(String username);
    	
    	/*
    	 * 插入语句
    	 */
    	public void insertUser(User user);
    
    	
    	/*
    	 * 更新语句
    	 */
    	public void updatePasswordByUsername(User user);
    	public void updatePasswordByEmail(User user);
    
    
  • UserMapper.xml映射xml配置文件:
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.sufe.mapper.UserMapper">
      
      <!-- 查询语句 -->
      <select id="getUserByUserId" parameterType="int" resultType="com.sufe.model.User">
    	SELECT * FROM User WHERE userId=#userId
      </select>
      <select id="getUserByUsername" parameterType="String" resultType="com.sufe.model.User">
    	SELECT * FROM User WHERE username=#username
      </select>
      <select id="test" parameterType="String" resultType="com.alibaba.fastjson.JSONObject">
    	SELECT * FROM User WHERE username=#username
      </select>
      <!-- 删除语句 -->
      <delete id="deleteUserByUserId" parameterType="int">
    	DELETE FROM User WHERE userId=#userId
      </delete>
      <delete id="deleteUserByUsername" parameterType="String">
    	DELETE FROM User WHERE username=#username
      </delete>
      <!-- 插入语句 -->
      <insert id="insertUser" parameterType="com.sufe.model.User">
    	INSERT INTO User(username,password,email,isProfessional,isAdministrator,profileURL) VALUES(#username,#password,#email,#isProfessional,#isAdministrator,#profileURL)
      </insert>
      <!-- 更新语句 -->
      <update id="updatePasswordByUsername" parameterType="com.sufe.model.User">
    	UPDATE User SET password=#password WHERE username=#username
      </update>
      <update id="updatePasswordByEmail" parameterType="com.sufe.model.User">
    	UPDATE User SET password=#password WHERE email=#email
      </update>
    </mapper>
    
  1. 为了便于测试我们再在com.sufe.controller包下编写一个UserController.java控制器类来指定路由,以及在com.sufe.service包下用一个带缓存注解器的服务类UserService.java封装Mapper里的接口函数:
  • UserController.java控制器类:
    package com.sufe.controller;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.alibaba.fastjson.JSONObject;
    import com.sufe.mapper.UserMapper;
    import com.sufe.model.User;
    import com.sufe.service.UserService;
    
    @Controller
    public class UserController 
    	
    	@Autowired
    	UserMapper userMapper;
    	
    	@Autowired
    	UserService userService;
    	
    	@ResponseBody
    	@GetMapping("/userquery/username") // 不带cache的查询
    	public List<User> getUser(@PathVariable("username") String username) 
    		return userMapper.getUserByUsername(username);
    	
    	
    	@ResponseBody
    	@GetMapping("/userquery/service/username") // 带cache的查询
    	public List<User> getUserByService(@PathVariable("username") String username) 
    		List<User> users = new ArrayList<User>();
    		users.add(userService.getUserByUsername(username));
    		return users;
    	
    	
    	@ResponseBody
    	@PostMapping("/userinsert") // 创建新用户
    	public User insertUser(HttpServletRequest request) 
    
    		// 获取请求参数
    		String username = request.getParameter("username");
    		String password = request.getParameter("password");
    		String email = request.getParameter("email");
    		String identity = request.getParameter("identity");
    
    		// 根据注册信息设置User Bean
    		User user = new User();
    		user.setUsername(username);
    		user.setPassword(password);
    		user.setEmail(email);
    		user.setProfessional(false);
    		user.setAdministrator(false);
    		user.setProfileURL(null);
    		
    		// 将注册新用户导入数据库
    		userService.insertUser(user);
    		return user;
    	
    	
    	@ResponseBody
    	@GetMapping("/userdelete/username")
    	public List<User> deleteUser(@PathVariable("username") String username) 
    		List<User> users = userMapper.getUserByUsername(username);
    		userMapper.deleteUserByUsername(username);
    		return users;
    	
    		
    	@ResponseBody
    	@GetMapping("/userdelete/service/username")
    	public List<User> deleteUserByService(@PathVariable("username") String username) 
    		List<User> users = userMapper.getUserByUsername(username);
    		userService.deleteUserByUsername(username);
    		return users;
    	
    	
    	@ResponseBody
    	@GetMapping("/userupdate/username&password")	
    	public List<User> updateUser(@PathVariable("username") String username, @PathVariable("password") String password) 
    		
    		User user = new User();
    		user.setUsername(username);
    		user.setPassword(password);
    		userMapper.updatePasswordByUsername(user);
    		List<User> users = userMapper.getUserByUsername(username);
    		return users;
    		
    	
    	@ResponseBody
    	@GetMapping("/userupdate/service/username&password")	
    	public List<User> updateUserByService(@PathVariable("username") String username, @PathVariable("password") String password) 
    
    		User user = new User();
    		user.setUsername(username);
    		user.setPassword(password);
    		userService.updateUserPassword(user);
    		List<User> users = userMapper.getUserByUsername(username);
    		return users;
    	
    
    
  • UserService.java服务类:
    • 该类本质上与UserMapper.java中的对应函数没有区别,仅仅是添加了一个缓存注解器@Cacheable @CachePut @CachePut,就可以通过比较调用UserService.javaUserMapper.java中对应函数来看出缓存之间的区别在哪里了,注意到上面UserController.java控制器类中所有路由里带/service的都是调用UserService.java中带缓存的方法,其他则是直接调用UserMapper.java中的方法;
    package com.sufe.service;
    
    import java.util.List;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    
    import com.sufe.mapper.UserMapper;
    import com.sufe.model.User;
    
    @Service
    public class UserService 
    	
    	@Autowired
    	UserMapper userMapper;
    	
    	Logger logger = LoggerFactory.getLogger(UserService.class);
    	
    	@Cacheable(value="user")
    	public User getUserByUsername(String username) 
    		logger.info("正在查询用户: "+username);
    		List<User> users = userMapper.getUserByUsername(username);
    		if (users.size()==0) 
    			return null;
    		
    		return users.get(0);
    	
    	
    	@CacheEvict(value="user", key="#username")
    	public void deleteUserByUsername(String username) 
    		logger.info("正在删除用户: "+username);
    		userMapper.deleteUserByUsername(username);
    		
    	
    	public void insertUser(User user) 
    		logger.info("正在插入用户: "+user);
    		userMapper.insertUser(user);
    	
    	
    	@CachePut(value="user", key="#result.username")
    	public User updateUserPassword(User user) 
    		logger.info("正在更新用户密码: "+user.getUsername());
    		userMapper.updatePasswordByUsername(user);
    		return user;
    	
    
    

1.2 测试流程

  1. 展示流程

  2. 启动项目, 清空控制台输出信息;

  3. 访问http://localhost:8080/userquery/test001

  • 页面出现test001用户的json信息;
  • 观察控制台输出, 有如下sql输出:
    2020-11-20 16:00:31.148 DEBUG 7284 --- [nio-8080-exec-4] c.s.mapper.UserMapper.getUserByUsername  : ==>  Preparing: SELECT * FROM User WHERE username=?
    2020-11-20 16:00:31.149 DEBUG 7284 --- [nio-8080-exec-4] c.s.mapper.UserMapper.getUserByUsername  : ==> Parameters: test001(String)
    
  • 清空控制台输出信息;
  1. 访问http://localhost:8080/userquery/service/test001
  • 页面出现test001用户的信息;
  • 观察控制台输出, 仍然有如下sql输出:
    2020-11-20 16:25:33.174 DEBUG 8420 --- [nio-8080-exec-1] c.s.mapper.UserMapper.getUserByUsername  : ==>  Preparing: SELECT * FROM User WHERE username=?
    2020-11-20 16:25:33.195 DEBUG 8420 --- [nio-8080-exec-1] c.s.mapper.UserMapper.getUserByUsername  : ==> Parameters: test001(String)
    
  • 清空控制台输出信息;
  • 迅速再运行一次, 发现没有sql输出, 说明使用了缓存:
    2020-11-20 16:25:48.560 DEBUG 8420 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : GET "/userquery/service/test001", parameters=
    2020-11-20 16:25:48.561 DEBUG 8420 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.sufe.controller.UserController#getUserByService(String)
    2020-11-20 16:25:48.563 DEBUG 8420 --- [nio-8080-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/json;q=0.8', given [text/html, application/xhtml+xml, image/webp, application/xml;q=0.9, */*;q=0.8] and supported [application/json, application/*+json, application/json, application/*+json]
    2020-11-20 16:25:48.563 DEBUG 8420 --- [nio-8080-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Writing [com.sufe.model.User@6a5548ff]
    2020-11-20 16:25:48.564 DEBUG 8420 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Completed 200 OK
    
  • 清空控制台输出信息;
  1. 访问http://localhost:8080/userquery/service/test001
  • 迅速访问http://localhost:8080/userupdate/test001&12345678
  • 页面出现test001用户密码修改后的信息;
  • 观察控制台输出, 有如下输出:
    2020-11-20 16:01:23.100 DEBUG 7284 --- [nio-8080-exec-6] c.s.m.U.updatePasswordByUsername         : ==>  Preparing: UPDATE User SET password=? WHERE username=?
    2020-11-20 16:01:23.101 DEBUG 7284 --- [nio-8080-exec-6] c.s.m.U.updatePasswordByUsername         : ==> Parameters: 123456(String), test001(String)
    
    • 这里也会有SELECT语句是因为在UserController.java控制器类中的对应函数里也写了查询的代码, 因为要返回更新后的用户信息, 所以顺带做了个usermapper不带缓存的查询;
  • 清空控制台输出;
  • 迅速再次访问http://localhost:8080/userquery/service/test001
    • 发现test001的密码没有发生改变;
    • 可重复测试发现确实一直使用缓存而不更新;
  • 但是访问http://localhost:8080/userquery/test001发现密码确实更新
  • 清空控制台输出;
  • 用http://localhost:8080/userupdate/service/test001&12345678作更新测试发现可以更新缓存;
    • 原因:
      • @Cacheput的功能是更新缓存,即会执行该注解器下函数的全部内容,然后将执行结果更新到@Cacheput注解器的key和value两个参数指定的缓存键中;
      • 注意此处的参数key值为#result.username, 这是SpEL表达式, #result指代函数返回结果;
  1. 用与3中同样的方法分别访问:
  • http://localhost:8080/userdelete/service/test001
    • 删除掉test001理论上就不能访问test001了;
    • 但是如果在访问http://localhost:8080/userdelete/service/test001事先访问了http://localhost:8080/userquery/service/test001, 就会发现再次访问http://localhost:8080/userquery/service/test001仍然可以查到test001(在缓存时限内)
  1. 关于在application.properties配置文件中配置缓存时效,以及能够让控制台在执行mapper文件输出sql语句的方法:
# cache expired
caching.specs.test.timeout=120
caching.specs.test.expireMode=0

# print sql when execute mapper
logging.level.com.sufe.mapper=debug
debug=true

2 SpringBoot整合Redis

Redis属于NoSQL的一种类型,它是一种建立在内存上的数据库,主要处理内存中的数据结构,因此是用来存储缓存数据非常好的一种方法;

  1. 安装Redis: