之前一段时间自学了Redis,进行一下总结
三个部分:
2.1)导入jedis.jar包
2.2)直接 Jedis jedis = new Jedis("192.168.80.131", 6379);获取jedis对象,然后进行操作
2.3)也可以创建jedis连接池JedisPoolUtil,然后需要的时候从连接池中get,使用完release即可
package com.ludada.redis.test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisPoolUtil {
// 被volatile修饰的变量不会被本地线程缓存,对该变量的读写都是直接操作共享内存。
private static volatile JedisPool jedisPool = null;
// 私有化静态方法,不能new
private JedisPoolUtil() {};
//对外提供一个get方法
public static JedisPool getJedisPoolInstance() {
if (jedisPool == null) {
synchronized (JedisPoolUtil.class) {
if (jedisPool == null) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxActive(1000);
poolConfig.setMaxIdle(32);
poolConfig.setMaxWait(100 * 1000);
poolConfig.setTestOnBorrow(true);
jedisPool = new JedisPool(poolConfig, "192.168.80.131",6379);
}
}
}
return jedisPool;
}
//释放回池子
public static void release(JedisPool jedisPool,Jedis jedis){
if(jedis != null){
jedisPool.returnResourceObject(jedis);
}
}
}
3.1)导入spring-data-redis.jar和jedis.jar
3.2)在spring的配置文件配置redisTemplate
3.3)使用redisTemplate
package ldd.Spring_Redis;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
public class myTest {
RedisTemplate redisTemplate = null;
@Before
public void before(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
redisTemplate = ctx.getBean(RedisTemplate.class);
}
@Test
public void Test01() {
Role role = new Role();
role.setId(1L);
role.setNote("note_1");
role.setRoleName("role_name_1");
//存储到到内存中的不是map而是string,进行了序列化
redisTemplate.opsForValue().set("role_1", role);
Role role1 = (Role) redisTemplate.opsForValue().get("role_1");
//上面两步不能保证每次使用RedisTemplate是操作同一个对Redis的连接
System.out.println(role1.toString());
}
@Test
public void Test02(){
final Role role = new Role();
role.setId(1L);
role.setNote("note_1");
role.setRoleName("role_name_1");
SessionCallback callback = new SessionCallback(){
public Role execute(RedisOperations ops) throws DataAccessException {
ops.boundValueOps("role_1").set(role);
return (Role) ops.boundValueOps("role_1").get();
}
};
Role savedRole = (Role) redisTemplate.execute(callback);
System.out.println(savedRole.getRoleName());
}
}
环境:jdk 1.8 + tomcat 7 + redis 3.0.4 + spring 4.2 + mybatis 3.3.0
先上demo(maven项目):
UserController:
处理器方法
对于数据一致性要求不高的数据,通过redisTemplate进行查找,添加。
对于数据一致性要求较高的数据,直接调用service层方法。
package com.ssm.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.ssm.pojo.User;
import com.ssm.service.IUserService;
/**
* user控制器
*
*/
@Controller
@RequestMapping("/UserCRUD")
public class UserController {
@Resource
private IUserService userService;
@Resource
private RedisTemplate redisTemplate;
/**
* 统计注册用户总数
* @param null
* @return String
* @throws Exception
*/
@RequestMapping("/indexMSG")
public String getIndexMSG(Model model) throws Exception{
String usersCount = null;
//尝试从缓存中取数据
usersCount = (String) redisTemplate.opsForValue().get("users_count");
if(usersCount == null){
//redis缓存中无数据,从数据库中查询,并放入redis缓存中,设置生存时间为1小时
System.out.println("从数据库中取当前已注册用户数量");
usersCount = Integer.toString(userService.selectUsersCount());
redisTemplate.opsForValue().set("users_count", usersCount, 1, TimeUnit.HOURS);
} else {
System.out.println("从redis缓存中取当前已注册用户数量");
}
List ids = null;
ids = userService.selectNowIds();
model.addAttribute("usersCount", usersCount);
model.addAttribute("ids", ids);
return "forward:/index.jsp";
}
/**
* 通过ID查询User
*
* @param userId
* @param model
* @return String
*/
@RequestMapping(value = "/getUserById", method = RequestMethod.GET)
public String getUserById(Model model, Integer userId) {
System.out.println("**********getUserById********");
User user = userService.getUserById(userId);
model.addAttribute("user", user); // 填充数据到model
return "showUser";
}
/**
* 查询所有User
*
* @param request
* @param model
* @return
*/
@RequestMapping(value = "/showUser", method = RequestMethod.GET)
public String showUsers(Model model) {
System.out.println("**********showUsers********");
List userList = new ArrayList();
userList = userService.getAllUser();
model.addAttribute("userList", userList); // 填充数据到model
return "showUser";
}
/**
* 增加一个用户
*
* @param userName
* @param sex
* @param age
* @throws Exception
*/
@RequestMapping(value = "/addUser", method = RequestMethod.POST)
public void addUser(User user, HttpServletResponse response) throws Exception {
System.out.println("******addUser********");
User result = userService.insertUser(user);
if (result.getId() != 0) {
response.getWriter().print("OK");
} else {
response.getWriter().print("FAIL");
}
}
/**
* 通过userID删除用户
*
* @param userID
* @throws Exception
*/
@RequestMapping(value = "/delUser/{userID}", method = RequestMethod.POST)
public void delUser(@PathVariable int userID, HttpServletResponse response) throws Exception {
System.out.println(userID);
int i = userService.deleteUser(userID);
if(i == 1){
response.getWriter().print("OK");
}else{
response.getWriter().print("删除失败!");
}
}
/**
* 查询用户
*
* @param model
* @param keyWords
* @return
*/
@RequestMapping("/search")
public ModelAndView findUsers(String keyWords) {
System.out.println(keyWords);
ModelAndView mv = new ModelAndView();
List userList = new ArrayList();
userList = userService.findUsers(keyWords);
mv.addObject("userList", userList);
mv.setViewName("showUser");
return mv;
}
/**
* 更新用户信息
* @param userName
* @param sex
* @param age
* @param id
* @return
* @throws Exception
*/
@RequestMapping(value="/editUser",method=RequestMethod.POST)
public void editUser(int id, String name, String sex, int age, HttpServletResponse res) throws Exception {
User user = new User();
user.setId(id);
user.setAge(age);
user.setsex(sex);
user.setUserName(name);
System.out.println(user.toString());
int rows = userService.editUser(user);
if(rows == 1){
res.getWriter().print("OK");
} else {
res.getWriter().print("FAIL");
}
}
}
UserMapper.java
package com.ssm.dao;
import java.util.List;
import com.ssm.pojo.User;
public interface UserMapper {
public User selectByPrimaryKey(Integer userId);
public List selectAllUser();
public int insertUser(User user);
public int deleteUser(int id);
public List findUsers(String keyWords);
public int editUser(User user);
public Integer selectUsersCount();
public List selectIds();
}
UserMapper.xml
id,userName,sex,age
INSERT INTO
tb_user(userName,sex,age) VALUES
(#{userName},#{sex},#{age})
DELETE FROM tb_user WHERE
id=#{id}
UPDATE tb_user
userName=#{userName},
sex=#{sex},
age=#{age}
WHERE id=#{id}
User.java
需要实现序列化接口
package com.ssm.pojo;
import java.io.Serializable;
/**
* User实体类对应数据库中的tb_user表
*
*/
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = -5244288298702801619L;
private int id;
private String userName;
private String sex;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getsex() {
return sex;
}
public void setsex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", sex=" + sex + ", age=" + age + "]";
}
}
IUserService.java
package com.ssm.service;
import java.util.List;
import com.ssm.pojo.User;
/**
* user表的操作接口
*
*/
public interface IUserService {
/**
* 通过id查询user接口方法
*
* @param userId
* @return User
*/
public User getUserById(Integer userId);
/**
* 查询所有的user
*
* @return 返回userList
*/
public List getAllUser();
/**
* 添加一个user
*
* @param user
*/
public User insertUser(User user);
/**
* 通过ID删除用户
*
* @param id
* @return
*/
public int deleteUser(int id);
/**
* 通过关键字查询用户
*
* @param keyWords
* @return
*/
public List findUsers(String keyWords);
/**
* 更新用户
*
* @param user
* @return int
*/
public int editUser(User user);
/**
* 查询网站注册用户数量(一小时更新一次)
* @return Integer
*/
public Integer selectUsersCount();
/**
* 查询现有ID
* @return List
*/
public List selectNowIds();
}
UserServiceImpl.java
最重要的便是serviceImpl类,
package com.ssm.serviceImpl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.ssm.dao.UserMapper;
import com.ssm.pojo.User;
import com.ssm.service.IUserService;
/**
*
* 缓存机制说明:所有有缓存注解的查询结果都放进了缓存,也就是把MySQL查询的结果放到了redis中去,
* 然后第二次发起该条查询时就可以从redis中去读取查询的结果,从而不与MySQL交互,从而达到优化的效果,
* redis的查询速度之于MySQL的查询速度相当于 内存读写速度 /硬盘读写速度
*
* @Cacheable(value="xxx" key="zzz")注解:标注该方法查询的结果进入缓存,再次访问时直接读取缓存中的数据
* 1.对于有参数的方法,指定value(缓存区间)和key(缓存的key);
* 对于无参数的方法,只需指定value,存到数据库中数据的key通过com.ssm.utils.RedisCacheConfig中重写的generate()方法生成。
* 2.调用该注解标识的方法时,会根据value和key去redis缓存中查找数据,如果查找不到,则去数据库中查找,然后将查找到的数据存放入redis缓存中;
* 3.向redis中填充的数据分为两部分:
* 1).用来记录xxx缓存区间中的缓存数据的key的xxx~keys(zset类型)
* 2).缓存的数据,key:数据的key;value:序列化后的从数据库中得到的数据
* 4.第一次执行@Cacheable注解标识的方法,会在redis中新增上面两条数据
* 5.非第一次执行@Cacheable注解标识的方法,若未从redis中查找到数据,则执行从数据库中查找,并:
* 1).新增从数据库中查找到的数据
* 2).在对应的zset类型的用来记录缓存区间中键的数据中新增一个值,新增的value为上一步新增的数据的key
*/
@Service("userService")
//事务注解
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public class UserServiceImpl implements IUserService {
@Resource
private UserMapper iUserDao;
/**
* 根据ID查找user
* 查到的数据存到users缓存区间,key为user_id,value为序列化后的user对象
*
*/
@Cacheable(value = "aboutUser", key="'user_'+#userId")
@Override
public User getUserById(Integer userId) {
return iUserDao.selectByPrimaryKey(userId);
}
/**
* 获取所有用户信息
* 1.对数据一致性要求较高,所以在执行增删改操作后需要将redis中该数据的缓存清空,
* 从数据库中获取最新数据。
* 2.若缓存中没有所需的数据,则执行该方法后:
* 1).在redis缓存中新增一条数据
* key:getAllUser value:序列化后的List
* key的值通过com.ssm.utils.RedisCacheConfig中重写的generate()方法生成
* 2).在用来记录aboutUser缓存区间中的缓存数据的key的aboutUser~keys(zset类型)中新添加一个value,
* 值为上面新增数据的key
*/
@Cacheable(value="aboutUser")
@Override
public List getAllUser() {
return iUserDao.selectAllUser();
}
/**
* @CacheEvict()注解:移除指定缓存区间的一个或者多个缓存对象
* @param value + key 或者 value + allEntries=true
* 1.value + key 移除value缓存区间内的键为key的数据
* 2.value + allEntries=true 移除value缓存区间内的所有数据
*/
//@CacheEvict(value= "aboutUser", key="'user_'+#result.id")
@CacheEvict(value= "aboutUser", allEntries=true)
@Override
public User insertUser(User user) {
iUserDao.insertUser(user);//进行了主键回填
return user;
}
/**
* 根据id删除用户
*/
@CacheEvict(value= "aboutUser", allEntries=true)
@Override
public int deleteUser(int id) {
return iUserDao.deleteUser(id);
}
/**
* 根据关键词模糊查询用户,命中率较低,不存入redis缓存中
*/
@Override
public List findUsers(String keyWords) {
return iUserDao.findUsers(keyWords);
}
@CacheEvict(value= {"aboutUser"},allEntries=true)
@Override
public int editUser(User user) {
return iUserDao.editUser(user);
}
/**
* 统计当前所有用户ID
* 1.对数据一致性要求较高,所以在执行增删改操作后需要将redis中该数据的缓存清空,
* 从数据库中获取最新数据。
* 2.执行该方法后,在redis缓存中新增一条数据
* 1) selectNowIds 缓存的数据的key,可以在com.ssm.utils.RedisCacheConfig中重写generate()方法自定义
* 3.然后在zset类型的aboutUser中添加一个值,值为上线的key
*/
@Cacheable(value = "aboutUser")
@Override
public List selectNowIds() {
return iUserDao.selectIds();
}
/**
* 统计注册用户个数
* 对数据一致性要求不高,所以在controller中使用redisTemplate存入redis,
* 并指定生存时间为1小时
*/
@Override
public Integer selectUsersCount() {
return iUserDao.selectUsersCount();
}
}
RedisCacheConfig.java
重写的generate()方法为那些需要将数据存入缓存的无参的方法指定存入缓存中的数据的key,可以根据需求自己设计。
package com.ssm.utils;
import java.lang.reflect.Method;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
/**
* 通过spring管理redis缓存配置
*
* @author Administrator
*
*/
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
private volatile JedisConnectionFactory jedisConnectionFactory;
private volatile RedisTemplate redisTemplate;
private volatile RedisCacheManager redisCacheManager;
public RedisCacheConfig() {
super();
}
/**
* 带参数的构造方法 初始化所有的成员变量
*
* @param jedisConnectionFactory
* @param redisTemplate
* @param redisCacheManager
*/
public RedisCacheConfig(JedisConnectionFactory jedisConnectionFactory, RedisTemplate redisTemplate,
RedisCacheManager redisCacheManager) {
this.jedisConnectionFactory = jedisConnectionFactory;
this.redisTemplate = redisTemplate;
this.redisCacheManager = redisCacheManager;
}
public JedisConnectionFactory getJedisConnecionFactory() {
return jedisConnectionFactory;
}
public RedisTemplate getRedisTemplate() {
return redisTemplate;
}
public RedisCacheManager getRedisCacheManager() {
return redisCacheManager;
}
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
//sb.append(target.getClass().getName());
sb.append(method.getName());
if(objects.length != 0){
sb.append("_");
for (Object obj : objects) {
sb.append(obj.toString());
}
}
return sb.toString();
}
};
}
}
applicationContext.xml
重点在缓存区间的配置,对应缓存注解中指定value的值;如不进行配置可value可以随意写,但是如果进行了配置value只能等于已配置的值
classpath:jdbc.properties
classpath:redis.properties
aboutUser
springmvc.xml
application/json;charset=UTF-8
jdbc.properties
jdbc.host=127.0.0.1
jdbc.database=user
jdbc.port=3306
jdbc.username=root
jdbc.password=111
log4j.properties
# Global logging configuration
log4j.rootLogger=WARN,stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c:%L - %m%n
# mybatis log
log4j.logger.com.ssm.dao.UserMapper=DEBUG
# lyz log
log4j.logger.com.ssm.controller=DEBUG
log4j.logger.com.ssm.service=DEBUG
redis.properties
# Redis settings
redis.host=192.168.80.132
redis.port=6379
#redis.pass=password
redis.dbIndex=0
redis.expiration=3000
redis.maxIdle=300
redis.maxActive=600
redis.maxWait=1000
redis.testOnBorrow=true
showUser.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
show user
showUser
未查找到用户信息!
id
userName
sex
age
${user.id }
${user.userName }
${user.sex }
${user.age }
id
userName
sex
age
${user.id }
${user.userName }
${user.sex }
${user.age }
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
SSM-Redis
当前注册用户数量(一个小时更新一次):
${usersCount }
pom.xml
4.0.0
ldd
SSMRedisDemo
war
0.0.1-SNAPSHOT
SSMRedisDemo Maven Webapp
http://maven.apache.org
UTF-8
4.2.0.RELEASE
org.springframework
spring-context
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-web
${spring.version}
javax.servlet
jstl
1.2
commons-logging
commons-logging
1.1.3
org.codehaus.jackson
jackson-mapper-asl
1.9.13
com.fasterxml.jackson.core
jackson-annotations
2.6.1
com.fasterxml.jackson.core
jackson-core
2.6.1
com.fasterxml.jackson.core
jackson-databind
2.6.1
org.springframework
spring-orm
${spring.version}
org.mybatis
mybatis-spring
1.2.3
mysql
mysql-connector-java
5.1.36
org.mybatis
mybatis
3.3.0
c3p0
c3p0
0.9.1.2
org.slf4j
slf4j-log4j12
1.7.12
log4j
log4j
1.2.17
org.springframework.data
spring-data-redis
1.6.0.RELEASE
redis.clients
jedis
2.7.3
maven-compiler-plugin
3.1
1.7
maven-war-plugin
2.4
WebContent
false
效果图:
然后简述一下Demo数据需求:
数据库很简单,只有一个user表,属性有自增的ID,字符串类型的用户名、性别,整型的年龄。
对于Demo中需求的数据分为两种:
一种是对一致性要求不高的,比如说页面头部的统计当前注册用户的总数,所以对于这种类型的数据,使用redisTemplate进行读取,先从redis中读取,读取不到的话就从数据库中读取,然后存放入redis缓存中,并设置生存时间,比如说1小时。
另一种是对一致性要求较高的,比如说现有的ID,因为这个ID列表需要输出到首页,供删除用户或者修改用户信息时参考,所以像这种不需要设置生存时间的数据,直接使用spring缓存放入redis中即可,然后执行增删改的时候,清空对应缓存即可。
缓存注解:
执行到serviceImp时,如果方法的上面有@Cacheable、@CachePut、@CacheEvict注解时,则表示该方法需要spring缓存管理,简单说一下这三个注解,
@Cacheable:表明在进入方法之前,Spring会先去缓存服务器中查找对应key的缓存值,如果找到缓存值,那么Spring将不会再调用方法,而是将缓存值独处,返回给调用者;如果没有找到缓存值,那么Spring就会执行你的方法,将最后的结果通过key保存到缓存服务器中。
@CachePut:表明Spring会将该方法返回的值缓存到缓存服务器中,这里需要注意的是,Spring不会事先去缓存服务器中查找,而是直接执行方法,然后缓存。换句话说,该方法始终会被Spring所调用。
@CacheEvict:表示执行方法后从缓存服务器移除对应key的值;
一般而言,对于查询,我们会考虑@Cacheable;对于插入和修改我们会考虑使用@CachePut;对于删除,我们会考虑@CacheEvict;
具体的使用方法可以请参考spring文档:
https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache-annotations
完整Demo下载 密码zb03
仅用于学习记忆与总结,如有错误的地方还请各位指出。