SpringBoot第六天 Redis的集成及如何通过Redis实现Mybatis的二级缓存

本文默认Redis已经安装完成,并且可以使用。这里不做Redis的安装等说明

第一步,创建一个基本的SpringBoot应用。我们需要在pom.xml加入Redis的相关依赖。redis的依赖为spring-boot-starter-data-redis和spring-boot-starter-jetty。如下面代码所示:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.qlys</groupId>
	<artifactId>spring-learn</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>


	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.15.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>



	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jetty</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.9</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!-- springboot 1.4之后开始支持 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>


	</dependencies>
</project>

我这里另外还使用Mybatis,Mysql,druid等依赖。因为我们下一步需要配置Mybatis的二级缓存。

下一步,修改appliation.yml文件,设置相关的参数。如:

server:
  port: 8888

logging:
  level: 
    com.qlys.dao: debug

spring: 
  datasource: 
    password: 123456
    username: qlys
    url: jdbc:mysql://127.0.0.1:3306/bjx-test?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    druid: 
      validation-query: select '*'
      initial-size: 1
      max-active: 20
      max-wait: 1000
      filters: stat
      pool-prepared-statements: true
      
      
mybatis: 
  mapper-locations: classpath*:com/qlys/dao/xml/*.xml
  type-aliases-package: com.qlys.dao.mapper


---
spring:
  redis:
    database: 9
    host: 127.0.0.1
    port: 6379
    password: 123456
    pool:
      max-idle: 20
      max-active: 20
      max-wait: -1
      min-idle: 0
    timeout: 1000

第三步 增加Redis的操作类,我把这个类建立的com.qlys.service包下面,代码如下:


package com.qlys.service;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

@Component
public class RedisUtils {

	@Autowired
	private RedisTemplate<Object, Object> redis;
	
	
	
	/*
	 * 读取
	 */
	public Object read(final Object key) {
		ValueOperations<Object,Object> ops = this.redis.opsForValue();
		return ops.get(key);
	}
	/*
	 * 写入
	 */
	public boolean set(final Object key,Object val) {
		ValueOperations<Object,Object> ops = this.redis.opsForValue();
		ops.set(key, val);
		return true;
	}
	
	/*
	 * 设置过期时间
	 */
	public boolean expire(final Object key,Object val,int expireTime) {
		ValueOperations<Object,Object> ops = this.redis.opsForValue();
		ops.set(key, val);
		this.redis.expire(key, expireTime, TimeUnit.SECONDS);
		return true;
	}
}

其中的RedisTemplate就是SpringBoot为我们封装好的操作Reids的工具类,我们常用的操作都是通过这个对象来完成的。在这个方法中,对于需要设置参数的存储时间是没有直接的方法的。因此我们需要先设置这个参数,然后设置这个参数的时间,即expire方法,先通过ops.set将数据写入redis,然后通过redis.expire方法设置过期时间。

第四步 然后我们就可以写一个代码来测试我们的Redis是否配置成功了,我建立了一个com.qlys.controller来编写界面的测试代码,代码如下:


package com.qlys.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.qlys.service.RedisUtils;


@RestController
public class TestController {

	@Autowired
	private RedisUtils redis;
	@RequestMapping("/save")
	public String writeRedis(String key) {
		redis.set("key", key);
		return "true";
	}
	@RequestMapping("/read")
	public Object readRedis() {
		System.out.println(redis.read("key"));
		return redis.read("key");
	}
}

 其中save方法用于将我们传的参数存入redis中,而read方法用于将我们存的值取出来,显示于界面上。

最后修改启动类,来测试我们是否配置成功,代码如下:


import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;


@SpringBootApplication
@ComponentScan(basePackages="com.qlys")
@MapperScan("com.qlys.dao.mapper")
public class Application {

	private static final Logger logger = LoggerFactory.getLogger(Application.class);
	
	public static void main(String[] args) {
		logger.info("系统启动");
		SpringApplication.run(Application.class, args);
	}
}

访问我们save和read功能,查看是否成功。如果成功,我们这可以开始我们的redis同mybatis整合操作。

既然要访问数据库,我们当然要写编写数据库相关的代码,由于集成mybatis前面已经讲过,这里我不详细说明,只针对Cache来进行说明

首先建立一个类,实现Cache接口,如下面代码所示:


package com.qlys.bean;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;


public class RedisCache implements Cache {

	private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
	
	
	private final ReadWriteLock lock = new ReentrantReadWriteLock(true);
	
	private static RedisTemplate<Object, Object> temp;
	
	public static void setRedisTemplate(RedisTemplate<Object, Object> temp) {
		RedisCache.temp = temp;
		logger.info("设置缓存对象--{}",temp);
	}
	
	private String id;
	@Override
	public String getId() {
		return id;
	}

	public RedisCache(final String id) {
		if (id == null) {
			throw new IllegalArgumentException("Cache instances require an ID");
		}
		this.id = id;
	}
	@Override
	public void putObject(final Object key,final Object value) {
		logger.info("写入缓存");
		lock.writeLock().lock();
		temp.opsForValue().set(key, value);
		lock.writeLock().unlock();
		
	}

	@Override
	public Object getObject(final Object key) {
		logger.info("读取缓存");
		return temp.opsForValue().get(key);
	}

	@Override
	public Object removeObject(final Object key) {
		logger.info("删除缓存");
		Object val = temp.opsForValue().get(key);
		temp.delete(key);
		return val;
	}

	@Override
	public void clear() {
  		logger.info("清除缓存");
		temp.execute((RedisCallback<Boolean>)(conn)->{
			conn.flushDb();
			return true;	
		});
	}
	

	@Override
	public int getSize() {
		logger.info("redis大小");
		return temp.execute((RedisCallback<Integer>)(conn)->{
			return conn.dbSize().intValue();
		});
	}

	@Override
	public ReadWriteLock getReadWriteLock() {
		return lock;
	}

}

修改上面的RedisUtils类,加入如下代码:

@Bean
	public RedisCache redisCache() {
		RedisCache.setRedisTemplate(redis);
		return null;
	}

这个代码是用于将Reids操作类存入到Cache操作类中,由于Cache是通过new创建的,无法通过注入完成。加入完成后,RedisUtils应该是这样的:


package com.qlys.service;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import com.qlys.bean.RedisCache;

@Component
public class RedisUtils {

	@Autowired
	private RedisTemplate<Object, Object> redis;
	
	@Bean
	public RedisCache redisCache() {
		RedisCache.setRedisTemplate(redis);
		return null;
	}
	
	/*
	 * 读取
	 */
	public Object read(final Object key) {
		ValueOperations<Object,Object> ops = this.redis.opsForValue();
		return ops.get(key);
	}
	/*
	 * 写入
	 */
	public boolean set(final Object key,Object val) {
		ValueOperations<Object,Object> ops = this.redis.opsForValue();
		ops.set(key, val);
		return true;
	}
	
	/*
	 * 设置过期时间
	 */
	public boolean expire(final Object key,Object val,int expireTime) {
		ValueOperations<Object,Object> ops = this.redis.opsForValue();
		ops.set(key, val);
		this.redis.expire(key, expireTime, TimeUnit.SECONDS);
		return true;
	}
}

最后在我们配置Mybatis语句的mapper.xml文件中加入缓存配置就可以了,代码如下:

 <cache eviction="FIFO" flushInterval="1" readOnly="false" type="com.qlys.bean.RedisCache

加入完成后,形成的代码如下:

<?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.qlys.dao.mapper.SystemMapper">
 <cache eviction="FIFO" flushInterval="1" readOnly="false" type="com.qlys.bean.RedisCache"></cache>
	<select id="select" resultType="map">
			SELECT * FROM a_table
		<where>
			<if test="id!=null">
				and a_id=#{id}
			</if>
		</where>
	</select>
	
	<insert id="insert" parameterType="map">
		INSERT INTO a_table(a_id,a_name,a_part) VALUES(#{id},#{name},#{part})
	</insert>
	
	<delete id="delete" parameterType="map">
		delete from a_table where a_id=#{id}
	</delete>
	
</mapper>

至此一个简单的Redis同mybatis集合方案算是完成了。

附:

由于我们有可能会对SpringBoot提供的RedisTemplate的key解析方案做一些改造,这里贴上一个我写的一个序列化方案,方便知道以后怎么修改RedisTemplate这个对象的相关参数。仅供参考:


package com.qlys.bean;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
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.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

@Configuration
@EnableCaching
public class RedisCacheConfig {

	private static final Logger logger = LoggerFactory.getLogger(RedisCacheConfig.class);
	
	@Bean
	public CacheManager cacheManager(RedisTemplate<Object, Object> redis) {
		return new RedisCacheManager(redis);
	}
	
	
	@Bean
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory){
		RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object,Object>();
		redisTemplate.setConnectionFactory(factory);
		RedisSerializer<Object> redisSerializer = new ByteRedisSerializer();
		redisTemplate.setKeySerializer(redisSerializer);
		redisTemplate.setHashKeySerializer(redisSerializer);
		return redisTemplate;
	}
	
	
	private class ByteRedisSerializer implements RedisSerializer<Object>{

		
		@Override
		public byte[] serialize(Object t) throws SerializationException {
			try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
					ObjectOutputStream oos = new ObjectOutputStream(baos)) {
				// 序列化
				oos.writeObject(t);
				return baos.toByteArray();
			} catch (Exception e) {
				logger.error(e.getMessage(),e);
			}
			return null;
		}

		@Override
		public Object deserialize(byte[] bytes) throws SerializationException {
			if (bytes == null) {
				return null;
			}
			try (InputStream bais = new ByteArrayInputStream(bytes);
					ObjectInputStream ois = new ObjectInputStream(bais)) {
				// 反序列化
				return ois.readObject();
			} catch (Exception e) {
				logger.error(e.getMessage(),e);
			}
			return null;
		}
		
	}
}

 

展开阅读全文

没有更多推荐了,返回首页