关于springboot中redis注解我想说
关于redis注解这个问题,实在是折磨了我好久,网上一大堆的资料但是,却没有一个来讲讲到底注解该怎么去存,写完注解数据会存储什么类型,以什么样的方式写注解才可以存入redis缓存
redis的注解问题查询了很多资料,都没找到适合我的,直到后来,我开始从基础开始了解了redis的数据类型后,才对注解有了自己的理解。
下面讲一下,在实际项目中用注解怎么实现信息的增删查改。
(首先声明一下:
我所谓的增删查改是对service进行的操作,
是对mysql数的增删查改,
并不是对redis数据库的增删查改)
首先说一下mysql数据的列表显示,
使用redis的目的:是将数据从mysql中取出,然后存入redis,当下次取数据时,先用过注解,去查寻redis数据库是否存在数据,如果存在你所需要的数据,那么,redis会直接取出数据,将不再去进行mysql数据库的查询。那也许有人会问了,为什么要用redis存数据呢,这样不是更麻烦吗?那是因为现在自己做的项目还小,感觉不出查询速度的差距,当数据量增大时,就能体现出mysql查询速度慢来了,redis就是用来解决这样的问题,一般说来,会被当作缓存使用。 因为它比数据库(mysql)快,所以常用的数据,可以考虑放在这里,这样就提高了性能。
好了回归正题:那么注解怎么使用呢?
Redis的缓存,我们都会在 Service 这一层上面做。 首先
我用BlogService来讲解
- 给分类加上如下注解,就表示分类在缓存里的keys,都是归 “blogs” 这个管理的。 通过工具Redis 图形界面客户端 可以看到有一个 blogs~keys, 就是它,用于维护分类信息在 redis里都有哪些 key.
@CacheConfig(cacheNames="blogs")
- 通过id获得博客,获取一个上面的注解是"@Cacheable(key="‘blog’+ #p0")", 第一次访问的时候, redis 是不会有数据的,所以就会通过 jpa 到数据库里去取出来, 一旦取出来之后,就会放在 redis里。 key 呢就是blog84 这个 key。 第二次访问的时候,redis 就有数据了,就不会从数据库里获取了。
@Cacheable(key="'blog'+ #p0")
public Blog getBlog(Long id) {
return blogRepository.findOne(id);
}
- 分页查询,这个和获取一个,其实没什么区别,就是key不一样,数据不再是一个对象,而是一个集合。 (保存在 redis 里是一个 json 数组)
,p0代表的时传参的第一个参数,#p0.offset代表的是pageable参数的开始id,#p0.pageSize代表的是pageable分页大小,如图所示blogs-page-0-5 就是第一页数据
@Cacheable(key="'blogs-page-'+#p0.offset+ '-' + #p0.pageSize")
public Page4Navigator<Blog> listBlog(Pageable pageable) {
Page<Blog> pageFromJPA= blogRepository.findAll(pageable);
Page4Navigator<Blog> page = new Page4Navigator<>(pageFromJPA,5);
return page;
}
- 增加,删除和修改这几个要好好讲一讲。 注意看,增加,删除和修改用的注解都是:@CacheEvict(allEntries=true),其意义是删除 blogs~keys 里的所有的keys. 可是呢,下面有各种有一行注释掉的注解。 比如增加,注释掉的注解是:// @CachePut(key="‘blog-one-’+ #p0")
它的作用是以 blog-one-id 的方式增加到 Redis中去。 这样做本身其实是没有问题的,而且在 get 的时候,还可以使用,但是最后还是放弃这种做法了,为什么呢?
因为,虽然这种方式可以在 redis 中增加一条数据,但是: 它并不能更新分页缓存 blogs-page-0-8 里的数据, 这样就会出现数据不一致的问题了。 即。在redis 中,有这一条单独数据的缓存,但是在分页数据里,却没有这一条,这样就矛盾了。
为了解决这个矛盾,最精细的做法是,同时更新分页缓存里的数据。 因为 redis 不是结构化的数据,它是 “nosql", 为了做到 同时更新缓存分页缓存里的数据,会超级的复杂,而且超级容易出错,其开发量也会非常大。
那么怎么办呢? 最后,我们采纳了折中的办法,即,一旦增加了某个分类数据,那么就把缓存里所有分类相关的数据,都清除掉。 下一次再访问的时候,一看,缓存里没数据,那么就会从数据库中读出来,读出来之后,再放在缓存里。如此这般,牺牲了一点小小的性能,数据的一致性就得到了保障了。
修改和删除,都是同一个道理。
@Transactional
@Override
@CacheEvict(allEntries=true)
public Blog saveBlog(Blog blog) {
if (blog.getId() == null) {
blog.setId((long) 0);
blog.setCreateTime(new Date());
blog.setUpdateTime(new Date());
blog.setViews(0);
} else {
blog.setUpdateTime(new Date());
}
return blogRepository.save(blog);
}
@Transactional
@Override
@CacheEvict(allEntries=true)
public void deleteBlog(Long id) {
blogRepository.delete(id);
}
@Transactional
@Override
@CacheEvict(allEntries=true)
public Blog updateBlog(Long id, Blog blog) {
Blog b = blogRepository.findOne(id);
if (b == null) {
throw new NotFoundException("该博客不存在");
}
BeanUtils.copyProperties(blog,b, MyBeanUtils.getNullPropertyNames(blog));
b.setUpdateTime(new Date());
return blogRepository.save(b);
}
所有源码:
@Service
@CacheConfig(cacheNames="blogs")
public class BlogServiceImpl implements BlogService {
@Autowired
private BlogRepository blogRepository;
/** * 通过id获得博客 * 获取一个上面的注解是 * "@Cacheable(key="'blog'+ #p0")" * 第一次访问的时候, redis 是不会有数据的,所以就会通过 jpa 到数据库里去取出来, * 一旦取出来之后,就会放在 redis里。 key 呢就是blogs84 这个 key。 * 第二次访问的时候,redis 就有数据了,就不会从数据库里获取了。 * @param id * @return */
@Override
@Cacheable(key="'blog'+ #p0")
public Blog getBlog(Long id) {
return blogRepository.findOne(id);
}
/** * * @param id * @return */
@Transactional
@Override
public Blog getAndConvert(Long id) {
Blog blog = blogRepository.findOne(id);
if (blog == null) {
throw new NotFoundException("该博客不存在");
}
Blog b = new Blog();
BeanUtils.copyProperties(blog,b);
String content = b.getContent();
b.setContent(MarkdownUtils.markdownToHtmlExtensions(content));
blogRepository.updateViews(id);
return b;
}
/** * 通过博客队列分页查询博客列表 * @param pageable * @param blog * @return */
@Override
@Cacheable(key="'blogs-BlogQuery-'+#p0+ '-page-' + #p1.offset+ '-' + #p1.pageSize")
public Page4Navigator<Blog> listBlog(BlogQuery blog,Pageable pageable) {
Page<Blog> pageFromJPA= blogRepository.findAll(new Specification<Blog>(){
@Override
public Predicate toPredicate(Root<Blog> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>();
if (!"".equals(blog.getTitle()) && blog.getTitle() != null) {
predicates.add(cb.like(root.<String>get("title"), "%"+blog.getTitle()+"%"));
}
if (blog.getTypeId() != null) {
predicates.add(cb.equal(root.<Type>get("type").get("id"), blog.getTypeId()));
}
if (blog.isRecommend()) {
predicates.add(cb.equal(root.<Boolean>get("recommend"), blog.isRecommend()));
}
cq.where(predicates.toArray(new Predicate[predicates.size()]));
return null;
}
},pageable);;
Page4Navigator<Blog> page = new Page4Navigator<>(pageFromJPA,5);
return page;
}
/** * 分页查询博客 * @param pageable * @return */
@Override
@Cacheable(key="'blogs-page-'+#p0.offset+ '-' + #p0.pageSize")
public Page4Navigator<Blog> listBlog(Pageable pageable) {
Page<Blog> pageFromJPA= blogRepository.findAll(pageable);
Page4Navigator<Blog> page = new Page4Navigator<>(pageFromJPA,5);
return page;
}
/** * 通过标签分页查询博客 * @param tagId * @param pageable * @return */
@Override
@Cacheable(key="'blogs-tagId-'+#p0+'-page-'+#p1.offset + '-' + #p1.pageSize ")
public Page4Navigator<Blog> listBlog(Long tagId,Pageable pageable) {
Page<Blog> pageFromJPA= blogRepository.findAll(new Specification<Blog>() {
@Override
public Predicate toPredicate(Root<Blog> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
Join join = root.join("tags");
return cb.equal(join.get("id"),tagId);
}
},pageable);
Page4Navigator<Blog> page = new Page4Navigator<>(pageFromJPA,5);
return page;
}
@Override
@Cacheable(key="'blogs-query-'+#p0+ '-page-' + #p1.offset+ '-' + #p1.pageSize")
public Page4Navigator<Blog> listBlog(String query,Pageable pageable) {
Page<Blog> pageFromJPA= blogRepository.findByQuery(query,pageable);
Page4Navigator<Blog> page = new Page4Navigator<>(pageFromJPA,5);
return page;
}
@Override
@Cacheable(key="'blogs-top-'+#p0")
public List<Blog> listRecommendBlogTop(Integer size) {
Sort sort = new Sort(Sort.Direction.DESC,"updateTime");
Pageable pageable = new PageRequest(0, size, sort);
return blogRepository.findTop(pageable);
}
@Override
public Map<String, List<Blog>> archiveBlog() {
List<String> years = blogRepository.findGroupYear();
Map<String, List<Blog>> map = new HashMap<>();
for (String year : years) {
map.put(year, blogRepository.findByYear(year));
}
return map;
}
@Override
@Cacheable(key="'blogs-count'")
public Long countBlog() {
return blogRepository.count();
}
@Transactional
@Override
@CacheEvict(allEntries=true)
public Blog saveBlog(Blog blog) {
if (blog.getId() == null) {
blog.setId((long) 0);
blog.setCreateTime(new Date());
blog.setUpdateTime(new Date());
blog.setViews(0);
} else {
blog.setUpdateTime(new Date());
}
return blogRepository.save(blog);
}
@Transactional
@Override
@CacheEvict(allEntries=true)
public Blog updateBlog(Long id, Blog blog) {
Blog b = blogRepository.findOne(id);
if (b == null) {
throw new NotFoundException("该博客不存在");
}
BeanUtils.copyProperties(blog,b, MyBeanUtils.getNullPropertyNames(blog));
b.setUpdateTime(new Date());
return blogRepository.save(b);
}
@Transactional
@Override
@CacheEvict(allEntries=true)
public void deleteBlog(Long id) {
blogRepository.delete(id);
}
}