上次我们其实是利用RedisTemplate实现的操作redis数据库实现增删改查,但是实际开发过程中不是把redis这种nosql作为底层存储数据库使用的,它的持久化性能没有之前我们使用的mysql数据库高,在实际的企业开发中通常将redis作为缓存中间件,当然我认为我们也可以将它作为消息队列使用,因为它的订阅与发布特性。
步骤
1,数据库的处理
2,mybatis注解的使用(老一套springboot使用mybatis的注解开发方式)
3,springCache注解的使用
4,添加测试运行查看结果
5,追踪源码看出端倪
1,数据库的处理
计划采取druid数据源使用mybatis框架管理数据库操作的api接口,采取redis数据库作为业务层的缓存。
yml文件配置:
spring:
redis:
主机IP
host: 192.168.20.150
密码
password: hlc
端口
port: 6379
lettuce:
池化管理
pool:
max-active: 50
max-wait: 3000
max-idle: 20
min-idle: 2
超时限制
timeout: 5000
选择数据库1
database: 1
静态资源引擎
thymeleaf:
mode: HTML
cache: true
数据源
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
max-active: 100
max-wait: 5000
min-idle: 2
mvc:
static-path-pattern: /static/**
tomcat运行端口
server:
port: 82
测试用数据表截图:
2,mybatis注解的使用(老一套springboot使用mybatis的注解开发方式)
这就是老一套的mybatis框架的开发方式了,实现基本的增删查:
@Mapper
public interface UserDao {
@Select("select * from test.user where name = #{username}")
User queryByName(String username);
@Insert("insert into test.user values (#{user.username},#{user.age})")
User save(User user);
@Delete("delete from test.user where name = #{username}")
void delete(String username);
}
3,springCache注解的使用
注解的使用很简单:
- 要想正常识别这个cache的注解,必须在启动类上添加@EnableCaching注解使boot项目能够识别其他的功能性注解。
- @Cacheable(value=" ", key=" "),放在业务层的select方法上,redis数据库按照key - value的格式将查询的数据存起来,下一次先找缓存,有就直接返回数据给用户,无则去找mysql数据库。
- @CachePut(value=" ", key=" ")放在业务层的insert方法上,你保存的数据在mysql里同时它会同步的在redis缓存里添加,便于快速查询,实现数据的热同步。
- @CacheEvict(key=" ",value=" ")放在业务层的delete方法上,删除mysql某一条数据的同时也会删除缓存里对应的数据。
测试代码:
启动类加注解@EnableCaching:
@SpringBootApplication
@EnableCaching
public class RedisCacheApplication {
public static void main(String[] args) {
SpringApplication.run(RedisCacheApplication.class, args);
}
}
业务层添加三个注解:
@Service("userService")
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
@Override
@Cacheable(key = "#username",value = "user")
public User queryByName(String username) {
return userDao.queryByName(username);
}
@Override
@CachePut(key = "#{user.username}", value = "user")
public User save(User user) {
return userDao.save(user);
}
@Override
@CacheEvict(value = "user" ,key = "#username")
public void delete(String username) {
userDao.delete(username);
}
}
4,添加测试运行查看结果
测试代码:
@SpringBootTest
class RedisCacheApplicationTests {
@Autowired
private UserService userService;
@Autowired
private RedisTemplate<String,Object> redisTemplate;
/*测试当我们操作mysql数据库时,缓存redis有没有写入对应的缓存key*/
@Test
void test1() {
userService.queryByName("横寺阳人");
User user = (User) redisTemplate.opsForValue().get("横寺阳人");
System.out.println(user);
}
@Test
void test2(){
userService.delete("横寺阳人");
}
}
运行test1结果:
运行test2结果:
5,追踪源码看出端倪
@EnableCaching注解源码:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Import;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({CachingConfigurationSelector.class})
public @interface EnableCaching {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default 2147483647;
}
可以发现Import注解与AdiveMode属于spring-context核心。所以可以大胆的认为springCahche是属于spring-context的内容,也就是spring自带的api。
需要特别注意的是我在测试里为了能够查看redis缓存内容我使用了Redistemplate模板,但是泛型<String,Object>会导致爆红,原因是该模板默认的泛型是<Object,Object>。可以自定义一个第三方的Bean来实现这个泛型(boot框架会将它自动注入容器进行匹配):
@Autowired
private RedisTemplate<String,Object> redisTemplate;
/*自定义的RedisTemplate模板配置实现键值的json数据序列化*/
@Configuration
public class redis_config {
@Bean/*自动装配Bean*/
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
/*定义方法的返回值,泛型自动匹配,后面的可以省略*/
RedisTemplate<String,Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
/*1.创建Jackson工具对象*/
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
/*2.为创建的工具对象设置Object映射*/
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
/*将key与hashKey设置为String的序列化方式*/
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
其实需要注意的是导入redis的数据不能直接是User类型的数据,需要在实体类实现序列化,当然也可以变成Json数据的形式。
序列化实体类只需要让实体类实现接口Serializable即可。
我们还需要注意的是我们采用可以转为json数据形式的模板并没有在业务层调用,业务层实现的是将数据库抽调或者是输入的数据直接以原本的形式导入缓存里,这就会出现某些编码冲突的问题。这个问题有待解决!