创建SpringBoot项目时不要乱选默认开发工具,有一个坑
<!--若创建时已选择,则不用导入--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
# 配置redis # Redis服务器地址 spring.redis.host=192.168.0.108 # Redis服务器连接端口 spring.redis.port=6379 # 使用数据库的索引编号,一个示例有16个数据库 0 到 15 spring.redis.database=0 # Redis服务器连接密码(默认为空) spring.redis.password=
com/yu/config/RedisConfig
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { // 将template 泛型设置为 <String, Object> RedisTemplate<String, Object> template = new RedisTemplate(); // 连接工厂,不必修改 template.setConnectionFactory(redisConnectionFactory); /* * 序列化设置 */ // key、hash的key 采用 String序列化方式 template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); /////////////////// // value、hash的value 采用 Jackson 序列化方式 template.setValueSerializer(RedisSerializer.json()); template.setHashValueSerializer(RedisSerializer.json()); template.afterPropertiesSet(); return template; } }
只要实体类进行了序列化,我们存什么都不会有乱码的担忧了。
//开发中都是序列化 public class User implements Serializable { private String name; private int age; }
@Autowired @Qualifier("redisTemplate") //确保使用的是自己定义的RedisTemplate private RedisTemplate redisTemplate; @Test void contextLoads() { User user = new User("小明", 12); //pojo redisTemplate.opsForValue().set("key1",user); System.out.println(redisTemplate.opsForValue().get("key1")); }
缓存:读多写少,多用于查询
本地缓存:存在应用服务器内存中的数据
分布式缓存:存在在当前应用服务器内存之外的数据
集群:将同一种服务的多个节点放在一起,共同对系统提供服务的过程
分布式系统:有多个不同的服务集群,共同对系统提供服务
mybatis中的应用级缓存(二级缓存):SQLSessionFactory级别缓存
开启方式:mapper.xml中
mybatis是里面有个
<!--引入依赖 spring data redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.19</version> </dependency>
server.port=8080 #redis 单节点(这个是单节点的配置,即主从复制或者没有主从配置) spring.redis.host=192.168.0.108 spring.redis.port=6377 spring.redis.database=0 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/toutiao?characterEncoding=UTF-8 spring.datasource.username=root spring.datasource.password=12345 mybatis.mapper-locations=classpath:com/yu/mapper/*.xml mybatis.type-aliases-package=com.yu.entity logging.level.com.yu.dao=debug
@SpringBootApplication //指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类 //添加@MapperScan(“com.yu.dao”)注解以后, //com.yu.dao包下面的接口类,在编译之后都会生成相应的实现类 @MapperScan("com.yu.dao") public class DistributedRedisMybatis01Application {...}
///////////////entity层com.yu.entity//////////// @Data @Accessors(chain = true) //链式访问,该注解设置chain=true, //生成setter方法返回this(也就是返回的是对象),代替了默认的返回void。 public class User implements Serializable { private String id; private String name; private Integer age; private Date bit; } ///////////////dao层com.yu.dao//////////// public interface UserDao { List<User> findAll(); }
<!--Mapper层resources.com.yu.mapper.UserDaoMapper.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.yu.dao.UserDao"> <select id="findAll" resultType="User"> select id,name,age,bir from t_user; </select> </mapper>
///////////////service层com.yu.service//////////// public interface UserService { List<User> findAll(); } ///////////////service层实现类//////////// @Service @Transactional //@Transactional注解在代码执行出错的时候能够进行事务的回滚。 public class UserServiceImpl implements UserService{ @Autowired private UserDao userDao; @Override public List<User> findAll() { return userDao.findAll(); } } ///////////////测试//////////// import org.junit.Test; //不要导错包 @SpringBootTest(classes = DistributedRedisMybatis01Application.class) @RunWith(SpringRunner.class) //@RunWith是Junit4提供的注解,将Spring和Junit链接了起来。 public class TestUserService { @Autowired private UserService userService; @Test //测试方法用public修饰 public void contextLoads() { userService.findAll().forEach(u-> System.out.println("u="+u)); } }
<mapper namespace="com.yu.dao.UserDao"> <!-- 开启mybatis二级缓存--> <cache/> </mapper>
测试
@Test public void contextLoads() { userService.findAll().forEach(u-> System.out.println("u="+u)); System.out.println("===================="); userService.findAll().forEach(u-> System.out.println("u="+u)); } 输出:1 Preparing: select id,name,age,bir from t_user; 2 Cache Hit Ratio [com.yu.dao.UserDao]: 0.5 击中缓存
mybatis底层默认使用的是org.apache.ibatis.cache.impl.PerpetualCache
实现。
通过mybatis默认cache源码可知,能使用自定义Cache类(implement Cache)对里面的方法进行实现。
使用:<cache type="xxx.RedisCache"/>
修改为: <cache type="com.yu.cache.RedisCache"/>
编写com.yu.cache.RedisCache:
public class RedisCache implements Cache { //id:当前放入缓存的mapper的namespace //id==com.yu.dao.UserDao private String id; //必须存在构造方法 public RedisCache(String id){ this.id = id; } //返回cache唯一标识 @Override public String getId() { return this.id; } //缓存放入值 @Override public void putObject(Object o, Object o1) { } //获取缓存中的数据 @Override public Object getObject(Object o) { return null; } //... }
但是还需要获取RedisTemplate工厂,编写获取工厂工具类
//用来获取springboot创建好的工厂 //首先要交给spring管理,@Configuration或: @Component //要拿到工厂。要实现ApplicationContextAware接口 public class ApplicationContextUtils implements ApplicationContextAware { //将工厂保留下来 private static ApplicationContext applicationContext; //将创建好的工厂以参数形式传递给这个类 @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } //提供在工厂中获取对象的方法 //如RedisTemplate redisTemplate public static Object getBean(String beanName){ return applicationContext.getBean(beanName); } }
public class RedisCache implements Cache { //id:当前放入缓存的mapper的namespace //id==com.yu.dao.UserDao private String id; //必须存在构造方法 public RedisCache(String id){ this.id = id; } //返回cache唯一标识 @Override public String getId() { return this.id; } //=========通过application工具类获取redisTemplat======== //并封装此方法 private RedisTemplate getRedisTemplate(){ RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate"); //放入key时的序列化方式 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); return redisTemplate; } //缓存放入值 @Override public void putObject(Object key, Object value) { System.out.println("key:"+key.toString()); System.out.println("value:"+value); //使用redishash类型作为缓存存储模型 key hashkey value getRedisTemplate().opsForHash().put(id.toString(),key.toString(),value); } //获取缓存中的数据 @Override public Object getObject(Object key) { //根据key 从redis的hash类型中获取数据 return getRedisTemplate().opsForHash().get(id.toString(),key.toString()); } ////注意:这个方法为mybatis保留方法 默认没有实现 后续版本可能会实现 @Override public Object removeObject(Object key) { return null;//根据指定key删除缓存 } //清空缓存 @Override public void clear() { System.out.println("清空缓存!"); //清空namespace getRedisTemplate().delete(id.toString());//清空缓存 } @Override public int getSize() { //获取hash中key value数量 return getRedisTemplate().opsForHash().size(id.toString()).intValue(); } }
<chche-ref namespace=xxx />
:用来将多个具有关联关系查询缓存放在一起处理
修改:EmpDaoMapper.xml
<mapper namespace="com.yu.dao.EmpDao"> <!-- 关联关系缓存的处理--> <cache-ref namespace="com.yu.dao.UserDao"/> <select id="findAll" resultType="Emp"> select id,name from t_emp; </select> </mapper>
对放入redis中的key进行优化,key的长度不能太长。
原来长度:1004332409:4292646850:com.yu.dao.EmpDao.findAll:0:2147483647:select id,name from t_emp;:SqlSessionFactoryBean
MD5加密后长度:``
1 处理后,会生成32位16进制字符串
2 不同内容文件经过md5进行加密,结果一定不一样
利用spring框架提供的md5工具类:
String key = "hello"; string str = DigestUtils.md5DigestAsHex(key.getBytes());
RedisCache类中封装一个优化方法:
//封装一个对key进行md5处理方法 private String getKeyToMD5(String key){ return DigestUtils.md5DigestAsHex(key.getBytes()); }
使用:
getRedisTemplate().opsForHash().put(id.toString(),getKeyToMD5(key.toString()),value); getRedisTemplate().opsForHash().get(id.toString(), getKeyToMD5(key.toString()));
设定缓存失效时间
//缓存放入值 @Override public void putObject(Object key, Object value) { System.out.println("key:" + key.toString()); System.out.println("value:" + value); //使用redishash类型作为缓存存储模型 key hashkey value getRedisTemplate().opsForHash().put(id.toString(),getKeyToMD5(key.toString()),value); if(id.equals("com.yu.dao.UserDAO")){ //缓存超时 client 用户 client 员工 getRedisTemplate().expire(id.toString(),1, TimeUnit.HOURS); } if(id.equals("com.yu.dao.CityDAO")){ //缓存超时 client 用户 client 员工 getRedisTemplate().expire(id.toString(),30, TimeUnit.MINUTES); } //.....指定不同业务模块设置不同缓存超时时间 }