SpringBoot整合Redis

Jedis

我们要使用Java来操作Redis

什么是Jedis是Redis官方推荐的java连接开发工具?使用Java操作Redis中间件!如果你要使用java操作redis,那么一定要对jedis十分的熟悉!

测试步骤:

  1. 导入对应的依赖
<!--导入jedis的包-->
    <dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>
  1. 编码测试:
  • 连接数据库
  • 操作命令
  • 断开连接!
public class TestRedis {
    public static void main(String[] args) {
        // 1、new Jedis 对象即可
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //jedis所有命令就是我们之前学习的所有指令!
        System.out.printf(jedis.ping());//输出PONG表示连接成功
    }
}

常用的API

String
List
Set
Hash
Zset

package com.geng;

import redis.clients.jedis.Jedis;

import java.util.concurrent.TimeUnit;

public class TestRedis {
    public static void main(String[] args) {
        // 1、new Jedis 对象即可
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //jedis所有命令就是我们之前学习的所有指令!
        System.out.printf(jedis.ping());//输出PONG表示连接成功
        jedis.flushDB();//清空当前库的所有数据
        System.out.println("========增加数据==========");
        System.out.println(jedis.set("key1","value1"));
        System.out.println(jedis.set("key2","value2"));
        System.out.println(jedis.set("key3","value3"));
        System.out.println("删除键key2"+jedis.del("key2"));
        System.out.println("获取键key2"+jedis.get("key2"));
        System.out.println("修改key1"+jedis.set("key1","value1Changed"));
        System.out.println("获取key1的值:"+jedis.get("key1"));
        System.out.println("在key3后面加入值:"+jedis.append("key3","End"));
        System.out.println("key3的值:"+jedis.get("key3"));
        System.out.println("增加多个键值对:"+jedis.mset("key01","value01","key02","value02","key03","value03"));
        System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
        System.out.println("删除多个键值对:"+jedis.del("key01","key02"));
        System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));

        jedis.flushDB();
        System.out.println("============新增键值对防止覆盖原先值=============");
        System.out.println(jedis.setnx("key1","value1"));
        System.out.println(jedis.setnx("key2","value2"));
        System.out.println(jedis.setnx("key2","value2-new"));
        System.out.println(jedis.get("key1"));
        System.out.println(jedis.get("key2"));//获取的是value2

        System.out.println("============新增键值对并设置有效时间=============");
        System.out.println(jedis.setex("key3",2,"value3"));
        System.out.println(jedis.get("key3"));
        //睡眠三秒后key3不再能获取到
        try{
            TimeUnit.SECONDS.sleep(3);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(jedis.get("key3"));

        System.out.println("========获取原值,更新为新值=========");
        System.out.println(jedis.getSet("key2","key2GetSet"));
        System.out.println(jedis.get("key2"));
        System.out.println("获取key2的值的字符串:" + jedis.getrange("key2",2,4));
    }
}

控制台输出:

PONG========增加数据==========
OK
OK
OK
删除键key21
获取键key2null
修改key1OK
获取key1的值:value1Changed
在key3后面加入值:9
key3的值:value3End
增加多个键值对:OK
获取多个键值对:[value01, value02, value03]
删除多个键值对:2
获取多个键值对:[null, null, value03]
============新增键值对防止覆盖原先值=============
1
1
0
value1
value2
============新增键值对并设置有效时间=============
OK
value3
null
========获取原值,更新为新值=========
value2
key2GetSet
获取key2的值的字符串:y2G

Process finished with exit code 0

所有的api命令,就是我们对应的上面学习的指令,一个都没有变化!

通过Jedis再次理解事务

package com.geng;

import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","kuangshen");
        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        try{
            multi.set("user1",result);
            multi.set("user2",result);

            multi.exec();//执行事务
        }catch (Exception e){
            multi.discard();//放弃事务
            e.printStackTrace();
        }finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();//关闭连接
        }
    }
}

控制台打印如下:

{"name":"kuangshen","hello":"world"}
{"name":"kuangshen","hello":"world"}

Process finished with exit code 0

现在我们在其中添加异常:

package com.geng;

import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","kuangshen");
        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        try{
            multi.set("user1",result);
            multi.set("user2",result);
            int i = 1/0;
            multi.exec();//执行事务
        }catch (Exception e){
            multi.discard();//放弃事务
            e.printStackTrace();
        }finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();//关闭连接
        }
    }
}

控制台打印如下:

java.lang.ArithmeticException: / by zero
	at com.geng.TestTX.main(TestTX.java:20)
null
null

Process finished with exit code 0

SpringBoot整合Redis

SpringBoot操作数据:spring-data jpa jdbc mongodb redis!
SpringData 也是和SpringBoot齐名的项目!

说明:在SpringBoot2.x之后,原来使用的jedis被替换为了lettuce

jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池!BIO
lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式

源码分析:

  1. 找到 spring.factories:spring-boot-autoconfigure-2.3.4.RELEASE.jar → META-INF → spring.factories
  2. 在 spring.factories 中搜索 redis
  3. 点进 RedisAutoConfiguration
/*
 * Copyright 2012-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure.data.redis;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Redis support.
 *
 * @author Dave Syer
 * @author Andy Wilkinson
 * @author Christian Dupuis
 * @author Christoph Strobl
 * @author Phillip Webb
 * @author Eddú Meléndez
 * @author Stephane Nicoll
 * @author Marco Aust
 * @author Mark Paluch
 * @since 1.0.0
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")//我们可以自己定义一个redisTemplate来替换这个默认的!
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
	//默认的 RedisTemplate 没有过多的设置,redis对象都是需要序列化!
	//两个泛型都是Object,Object的类型,我们后续使用需要强制转换<String,Object>
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean //由于String是redis中最常使用的类型,所以说单独提出来了一个bean!
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

SpringBoot所有的配置类,都有一个自动配置类,自动配置类都会绑定一个properties配置文件

整合测试一下

1、导入依赖

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

2、配置连接

#配置redis
spring.redis.host=127.0.0.1
spring.redis.port=6379

3、测试!

package com.itgeng.springboot_redis;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class SpringbootRedisApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
		
		//在企业开发中,我们80%的情况下,都不会使用这个原生的方式去编写代码!
		//RedisUtils;
		
        //redisTemplate  操作不同的数据类型
        //opsForValue 操作字符串 类似String
        //opsForList 操作List 类似List
        //opsForSet
        //opsForHash
        //opsForZSet
        //opsForGeo
        //opsForHyperLogLog

        //除了基本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务,和基本的CRUD

        //获取
//        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//        connection.flushDb();
//        connection.flushAll();
        redisTemplate.opsForValue().set("mykey","我的值");
        System.out.println(redisTemplate.opsForValue().get("mykey"));
    }

}
我的值

在redis客户端输入keys * 得到以下结果:

redis 127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x05mykey"

为什么要序列化?

向数据库中插入了一个中文字符串,虽然在 Java 端可以看到返回了中文,但是在 Redis 中查看是一串乱码。

真实开发redis都是我们自定义的redisTemplate

pojo类:

package com.itgeng.springboot_redis.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

import java.io.Serializable;

@Component
@AllArgsConstructor
@NoArgsConstructor
@Data
//在企业中,我们所有的pojo都会序列化!SpringBoot
public class User implements Serializable {

    private String name;
    private int age;

}

redisTemplate模板:

@Configuration
public class RedisConfig {
    /**
     *  编写自定义的 redisTemplate
     *  这是一个比较固定的模板
     */
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        // 为了开发方便,直接使用<String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);

        // Json 配置序列化
        // 使用 jackson 解析任意的对象
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        // 使用 objectMapper 进行转义
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key 采用 String 的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // Hash 的 key 采用 String 的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value 采用 jackson 的序列化方式
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // Hash 的 value 采用 jackson 的序列化方式
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 把所有的配置 set 进 template
        template.afterPropertiesSet();

        return template;
    }
}

在我们真实开发中,或者你们公司,一般都可以看到一个公司自己封装的RedisUtil工具类。
这样调用

@Autowire
private RedisUtil redisUtil;

狂神RedisUtils工具类

所有的redis操作,其实对于java开发人员来说,十分的简单,更重要的是要去理解redis的思想和每一种数据结构的用处和作用场景!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值