Spring、Spring Boot集成Spring Data Redis以及相关API的使用

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 Spring、Spring Boot集成Spring Data Redis以及相关API的使用,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

Spring Data Redis

概述

Spring-data-redis是spring家族的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。

Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用。可以在Redis官网下载。

高度封装的RedisTemplate类

针对jedis客户端中大量api进行了归类封装,提供了一个高度封装的RedisTemplate类,包含了对5种数据类型的操作

// 操作字符串
RedisTemplate redisTemplate.opsForValue() 
//  操作hash
RedisTemplate redisTemplate.opsForHash() 
// 操作list
RedisTemplate redisTemplate.opsForList() 
// 操作set
RedisTemplate redisTemplate.opsForSet() 
// 操作有序set	
RedisTemplate redisTemplate.opsForZSet() 

Lettuce和Jedis的区别

Jedis是一个优秀的基于Java语言的Redis客户端。但是:Jedis在实现上是直接连接 Redis Server,在多个线程间共享一个Jedis实例时是线程不安全的,如果要在多线程场景下使用Jedis,需要使用连接池,每个线程都使用自己的Jedis 实例,当连接数量增多时,会消耗较多的物理资源。

Lettuce是基于Netty的连接,是一个可伸缩的线程安全的Redis 客户端,支持同步、异步和响应式模式。多个线程可以共享一个连接实例,而不必担心多线程并发问题。它基于优秀Netty NIO框架构建,支持Redis的高级功能,如Sentinel,集群,流水线,自动重新连接和 Redis 数据模型。

注意:

1.x的版本默认采用的连接池技术是Jedis

2.0以上版本默认连接池是Lettuce

Spring集成Spring Data Redis

引入依赖

<dependencies>
        <!-- 缓存 Redis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>

        <!--spring-data-redis-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

        <!--test-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.9.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <!-- Test dependencies -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

创建redis-config.properties

#redisIP地址
redis.host=127.0.0.1
#redis端口
redis.port=6379 
#redis密码
redis.pass=
#选择使用的DB
redis.database=6
#最大空闲数
redis.maxIdle=200 
#连接时的最大等待毫秒数
redis.maxWait=2000 
#在获取jedis实例时,是否进行验证操作;如果为true,则得到的jedis实例均是可用的
redis.testOnBorrow=true 

创建spring-redis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        ">

    <!--加载解析配置文件-->
    <context:property-placeholder location="classpath:redis-config.properties" />

    <!--Jedis连接池配置-->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}" />
        <property name="maxWaitMillis" value="${redis.maxWait}" />
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
    </bean>

    <!--
        Jedis连接工厂对象
        host-name:redis的IP
        port:端口号
        password:密码
        pool-config-re:连接池信息配置
     -->
    <bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
          p:host-name="${redis.host}"
          p:port="${redis.port}"
          p:password="${redis.pass}"
          p:pool-config-ref="poolConfig"/>

    <!--Jedis对缓存操作的模板对象-->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <!--配置数据源-->
        <property name="connectionFactory" ref="JedisConnectionFactory" />
    </bean>
</beans>

String类型操作

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class ValueTest {

    @Autowired
    private RedisTemplate redisTemplate;


    /***
     * 增加数据测试
     */
    @Test
    public void testAdd() {
        //boundValueOps用于操作简单的key:value类型
        redisTemplate.boundValueOps("value").set("小白");
    }


    /***
     * 查询操作
     */
    @Test
    public void testGet() {
        Object value = redisTemplate.boundValueOps("value").get();
        System.out.println(value);
    }

    /***
     * 删除测试
     */
    @Test
    public void testDelete() {
        //redisTemplate.boundValueOps("value")
        redisTemplate.delete("value");
    }
}

设置过期时间

存值时指定过期时间和时间单位,其他类型操作类似。

	/**
     * 存值,同时设置过期时间
     */
    @Test
    public void setValue(){
        redisTemplate.boundValueOps("name").set("小白");
        redisTemplate.boundValueOps("name").expire(10,TimeUnit.SECONDS);
    }

Set类型操作

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class SetTest {

    @Autowired
    private RedisTemplate redisTemplate;


    /***
     * 增加数据测试
     */
    @Test
    public void testAdd() {
        redisTemplate.boundSetOps("set").add("小白");
        redisTemplate.boundSetOps("set").add("小黑");
        redisTemplate.boundSetOps("set").add("小白");
        redisTemplate.boundSetOps("set").add("小黑");
    }


    /***
     * 查询操作
     */
    @Test
    public void testGet() {
        Set members = redisTemplate.boundSetOps("set").members();
        System.out.println(members);
    }

    /***
     * 删除测试
     */
    @Test
    public void testDelete() {
        redisTemplate.boundSetOps("set").remove("小黑");
    }

	/***
     * 删除整个集合
     */
    @Test
    public void testDeleteAll() {
        redisTemplate.delete("set");
    }
}

List类型操作

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class ListTest {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 左压栈:后添加的对象排在前边
     * 从队列的左边开始增加数据,
     */
    @Test
    public void testLeftAdd() {
        //只添加1条数据
        redisTemplate.boundListOps("list").leftPush("小白1");
        //批量添加
        redisTemplate.boundListOps("list").leftPushAll("小白2", "小白3", "小白4");
    }

    /***
     * 查询数据
     */
    @Test
    public void testGetValueLeft() {
        List list = redisTemplate.boundListOps("list").range(0, 10);
        System.out.println(list);
    }

    /**
     * 移除集合某个元素
     */
    @Test
    public void testRemoveByIndex() {
        redisTemplate.boundListOps("list").remove(1, "小白1");
    }

    /**
     * 查询集合某个元素
     */
    @Test
    public void testSearchByIndex() {
        String s = (String) redisTemplate.boundListOps("list").index(1);
        System.out.println(s);
    }


    /***
     * 左压出栈
     */
    @Test
    public void testLeftGet() {
        Object result = redisTemplate.boundListOps("list").leftPop();
        System.out.println(result);
    }


    /**
     * 右压栈
     */
    @Test
    public void testRightAdd() {
        //只添加1条数据
        redisTemplate.boundListOps("list").rightPush("小白1");
        //批量添加
        redisTemplate.boundListOps("list").rightPushAll("小白2", "小白3", "小白4");
    }


    /***
     * 查询数据
     */
    @Test
    public void testGetValueRight() {
        List list = redisTemplate.boundListOps("list").range(0, 10);
        System.out.println(list);
    }

    /***
     * 右压出栈
     */
    @Test
    public void testRightGet() {
        Object result = redisTemplate.boundListOps("list").rightPop();
        System.out.println(result);
    }
}

Hash类型操作

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class HashTest {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testAdd() {
        redisTemplate.boundHashOps("hash").put("1", "小白1");
        redisTemplate.boundHashOps("hash").put("2", "小白2");
        redisTemplate.boundHashOps("hash").put("3", "小白3");

        redisTemplate.boundHashOps("hashs").put("1", "小白1");
        redisTemplate.boundHashOps("hashs").put("2", "小白2");
        redisTemplate.boundHashOps("hashs").put("2", "小白3");
    }


    /***
     * 根据KEY提取值
     */
    @Test
    public void testGet() {
        Object result1 = redisTemplate.boundHashOps("hash").get("1");
        System.out.println(result1);

        Object result2 = redisTemplate.boundHashOps("hashs").get("1");
        System.out.println(result2);
    }


    /***
     * 根据KEY移除值
     */
    @Test
    public void testDelete() {
        redisTemplate.boundHashOps("hash").delete("2");
    }

    /***
     * 查询所有所有值
     */
    @Test
    public void testSetValue() {
        List nameSpace = redisTemplate.boundHashOps("hash").values();

        for (Object o : nameSpace) {
            System.out.println(o);
        }
    }
    
     /***
     * 查询所有key
     */
    @Test
    public void testGetKeys(){
        Set s = redisTemplate.boundHashOps("hash").keys();
        System.out.println(s);
    }
}

ZSet类型操作

zset是set的升级版本,它在set的基础上增加了一格顺序属性,这一属性在添加元素 的同时可以指定,每次指定后,zset会自动重新按照新的值调整顺序。可以理解为有两列 的mysql表,一列存储value,一列存储分值。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class ValueTest {

    @Autowired
    private RedisTemplate redisTemplate;


    /***
     * 存值 ,指定分值
     */
    @Test
    public void setValue() {
        redisTemplate.boundZSetOps("zset").add("小白",100);
        redisTemplate.boundZSetOps("zset").add("小黑",50);
        redisTemplate.boundZSetOps("zset").add("小红",80);
    }


    /***
     * 查询,由低到高
     */
    @Test
    public void getValue1() {
       Set namezset = redisTemplate.boundZSetOps("set").range(0,1);
    }

    /***
     * 查询,由高到低,取前10
     */
    @Test
    public void getValue2() {
       Set namezset =redisTemplate.boundZSetOps("set").reverseRange(0,10);
    }

	/***
     * 增加分数
     */
    @Test
    public void addScore() {
      redisTemplate.boundZSetOps("set").incrementScore("小黑",100);
    }

	/***
     * 查询值和分数
     */
    @Test
    public void addScore() {
     Set<ZSetOperations.TypedTuple> set=redisTemplate.boundZSetOps("set").reverseRangeWithScores(0,1);
        for(ZSetOperations.TypedTuple typedTuple:set){
            System.out.print("姓名:"+typedTuple.getValue());
            System.out.println("分数:"+typedTuple.getScore());
    }
}

StringRedisTemplate与RedisTemplate

1.StringRedisTemplate继承RedisTemplate。StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。

2.StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的,RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。

3.当存取字符串类型数据时,使用StringRedisTemplate;当数据是复杂的对象类型时,取出时又不想做任何数据转换,直接从Redis里面取出一个对象,则使用RedisTemplate。

自定义模板解决序列化问题

序列化方案

RedisTemplate<Object, Object>默认序列化使用的是JdkSerializationRedisSerializer ,存储二进制字节码。通常需要自定义模板,当自定义模板后又想存储String 字符串时,可以使StringRedisTemplate的方式。

1.JdkSerializationRedisSerializer

使用JDK提供的序列化功能。 优点是反序列化时不需要提供类型信息(class),但缺点是序列化后的结果非常庞大,是JSON格式的5倍左右,会消耗 Redis服务器的大量内存。

2.Jackson2JsonRedisSerializer

使用Jackson库将对象序列化为JSON字符串。优点是速度快,序列化后的字符串短小精悍。但缺点也非常致命,那就是类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。

3.GenericJackson2JsonRedisSerializer

GenericJackson2JsonRedisSerializer通用型序列化,这种序列化方式不用自己手动指定对象的 Class。

自定义模板

@Configuration
public class RedisConfig {

	@Bean
	public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory){
		RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
		//为string类型的key设置序列化
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		//为string类型的value设置序列化
		redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
		//为hash类型的key设置序列化
		redisTemplate.setHashKeySerializer(new StringRedisSerializer());
		//为hash类型的value设置序列化
		redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
		redisTemplate.setConnectionFactory(lettuceConnectionFactory);
		return redisTemplate;
	}
}

序列化测试

	@Test
	public void testSerial(){
		User user = new User();
		user.setId(1);
		user.setName("zhangsan");
		user.setAge(20);
		ValueOperations ops = redisTemplate.opsForValue();
		ops.set("user",user);
		Object user1 = ops.get("user");
		System.out.println(user1);
	}

Spring Boot集成Spring Data Redis

添加依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

注意:1.x的版本默认采用的连接池技术是Jedis;2.0以上版本默认连接池是Lettuce,如果采用Jedis,需要排除Lettuce的依赖。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
	<exclusions>
		<exclusion>
			<groupId>io.lettuce</groupId>
			<artifactId>lettuce-core</artifactId>
		</exclusion>
	</exclusions>
	
	<dependency>
		<groupId>redis.clients</groupId>
		<artifactId>jedis</artifactId>
	</dependency>
</dependency>

application.yml配置

spring:
  redis:
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器端口
    port: 6379
    # Redis认证密码
    password: root
    # Redis数据库
    database: 0
    # 连接超时时间
    timeout: 10000ms
    lettuce:
      pool:
        # 最大连接数,默认8
        max-active: 1024
        # 最大连接阻塞等待时间,单位毫秒,默认-1ms
        max-wait: 10000ms
        # 最大空闲连接,默认8
        max-idle: 200
        # 最小空闲连接,默认0
        min-idle: 5

集成测试

@SpringBootTest
public class DemoApplicationTests {

	@Autowired
	private RedisTemplate redisTemplate;
	@Autowired
	private StringRedisTemplate stringRedisTemplate;


	/**
	 * 测试连接
	 */
	@Test
	public void initConn() {
		ValueOperations ops = redisTemplate.opsForValue();
		ops.set("name","zhangsan");
		ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
		stringStringValueOperations.set("age","20");
		String age = stringStringValueOperations.get("age");
		System.out.println(age);
	}
	
 	/**
	 * 获取所有的key
	 */
	@Test
	public void testAllKey(){
		//获取当前数据库所有key
		Set keys = redisTemplate.keys("*");
		keys.forEach(System.out::println);
	}


	/**
	 * 失效时间
	 */
	@Test
	public void testExpire(){
		ValueOperations ops = redisTemplate.opsForValue();
		//方法一,添加key的时候设置失效时间
		// ops.set("code","test",30, TimeUnit.SECONDS);
		//方法二,给已经存在的key设置失效时间
		redisTemplate.expire("address",30, TimeUnit.SECONDS);
		//查看失效时间
		Long expire = redisTemplate.getExpire("address");
		System.out.println(expire);
	}

}

String类型操作

	@Test
	public void testString(){
		ValueOperations ops = redisTemplate.opsForValue();
		//添加一条数据
		ops.set("name","zhangsan");

		//获取一条数据
		String name = (String) ops.get("name");
		System.out.println(name);
		//层级关系,目录形式存储数据
		ops.set("user:01","lisi");
		//添加多条数据
		Map<String,String> map = new HashMap<>();
		map.put("age","20");
		map.put("address","sh");
		ops.multiSet(map);
		//获取多条数据
		List<String> keys = new ArrayList<>();
		keys.add("name");
		keys.add("age");
		keys.add("address");
		List list = ops.multiGet(keys);
		list.forEach(System.out::println);
		//删除数据
		redisTemplate.delete("name");
	}

Hash类型操作

	@Test
	public void testHash(){
		HashOperations hashOperations = redisTemplate.opsForHash();
		/**
		 * 添加一条数据
		 * redis的key;hash的key;hash的value
		 */
		hashOperations.put("user","name","zhangsan");
		/**
		 * 获取一条数据
		 * redis的key;hash的key
		 */
		hashOperations.get("user","name");
		
		//添加多条数据
		Map<String,String> map = new HashMap<>();
		map.put("age","20");
		map.put("address","sh");
		hashOperations.putAll("user",map);
		
		//获取多条数据
		List<String> keys = new ArrayList<>();
		keys.add("name");
		keys.add("age");
		keys.add("address");
		List user = hashOperations.multiGet("user", keys);
		user.forEach(System.out::println);
		
		//获取hash类型的所有数据
		Map<String,String> entries = hashOperations.entries("user");
		entries.entrySet().forEach(e->{
			System.out.println(e.getKey()+"-->"+e.getValue());
		});
		
		//hash的删除数据
		hashOperations.delete("user","name","age");
	}

List类型操作

	@Test
	public void testList(){
		ListOperations listOperations = redisTemplate.opsForList();
		//左添加
		listOperations.leftPush("students","wangwu");
		listOperations.leftPush("students","lisi");		
		/**
		 * 左添加
		 * redis的key;被左添加的数据;添加的数据,添加到第二个参数的左边
		 */
		listOperations.leftPush("students","wangwu","zhangsan");
		//右添加
		listOperations.rightPush("students","zhaoliu");
		
		//获取数据
		List list = listOperations.range("students", 0, 8);
		list.forEach(System.out::println);
		
		//获取总条数
		Long size = listOperations.size("students");
		System.out.println(size);
		
		//删除数据
		listOperations.remove("students",1,"lisi");
		
		//左弹出
		listOperations.leftPop("students");
		//右弹出
		listOperations.rightPop("students");
	}

Set类型操作

	@Test
	public void testSet(){
		SetOperations setOperations = redisTemplate.opsForSet();
		//添加数据
		String[] letters = new String[]{"aaa","bbb","ccc","ddd","eee"};
		// setOperations.add("letters","aaa","bbb","ccc","ddd","eee");
		setOperations.add("letters",letters);
		//获取数据
		Set set = setOperations.members("letters");
		set.forEach(System.out::println);
		//删除数据
		setOperations.remove("letters","aaa","bbb");
	}

ZSet类型操作

	@Test
	public void testSortedSet(){
		ZSetOperations zSetOperations = redisTemplate.opsForZSet();
		//添加数据
		ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<>("zhangsan",7D);
		ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<>("lisi",3D);
		ZSetOperations.TypedTuple<Object> objectTypedTuple3 = new DefaultTypedTuple<>("wangwu",5D);
		ZSetOperations.TypedTuple<Object> objectTypedTuple4 = new DefaultTypedTuple<>("zhaoliu",1D);
		ZSetOperations.TypedTuple<Object> objectTypedTuple5 = new DefaultTypedTuple<>("tianqi",10D);
		Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>();
		tuples.add(objectTypedTuple1);
		tuples.add(objectTypedTuple2);
		tuples.add(objectTypedTuple3);
		tuples.add(objectTypedTuple4);
		tuples.add(objectTypedTuple5);
		zSetOperations.add("score",tuples);
		//获取数据
		Set set = zSetOperations.range("score", 0, 4);
		set.forEach(System.out::println);
		//获取总条数
		Long size = zSetOperations.size("score");
		System.out.println(size);
		zSetOperations.remove("score","zhangsan","lisi");
	}

Spring Data Redis整合使用哨兵机制

application.yml配置

spring:
  redis:
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器端口
    port: 6379
    # Redis认证密码
    password: root
    # Redis数据库
    database: 0
    # 连接超时时间
    timeout: 10000ms
    lettuce:
      pool:
        # 最大连接数,默认8
        max-active: 1024
        # 最大连接阻塞等待时间,单位毫秒,默认-1ms
        max-wait: 10000ms
        # 最大空闲连接,默认8
        max-idle: 200
        # 最小空闲连接,默认0
        min-idle: 5
    # 集群配置,哨兵模式
    sentinel:
      #主节点名称
      master: mymaster
      #节点
      nodes:  127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381

Bean注解配置

@Configuration
public class RedisConfig {

	@Bean
	public RedisSentinelConfiguration redisSentinelConfiguration(){
		RedisSentinelConfiguration redisSentinelConfiguration =
				new RedisSentinelConfiguration()
						//主节点名称
						.master("mymaster")
						//哨兵
						.sentinel("127.0.0.1",6379)
						.sentinel("127.0.0.1",6380)
						.sentinel("127.0.0.1",6381);
		//密码
		redisSentinelConfiguration.setPassword("root");
		return redisSentinelConfiguration;
	}
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/137096.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!