springboot入门之八 数据缓存之Redis Mongo

8 篇文章 2 订阅
1 篇文章 0 订阅

1. 整合redis

依赖导入

redis的引用包:

	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
	<!--session管理委托给redis-->
		<dependency>
		    <groupId>org.springframework.session</groupId>
		    <artifactId>spring-session-data-redis</artifactId>
		</dependency>
		<!--集群管理使用jedis-->
		<dependency>
		    <groupId>redis.clients</groupId>
		    <artifactId>jedis</artifactId>
		</dependency>

1.1 单机redis

配置文件


server:
   port: 8081


spring:
    redis:

      ##单机redis  
      # Redis数据库索引(默认为0)  
      database : 0  
      # Redis服务器地址  
      host : localhost  
      # Redis服务器连接端口  
      port : 6379  
      # Redis服务器连接密码(默认为空)  
      password :   
      # 连接超时时间(毫秒)  
      timeout : 1000 

1.2 redis启动器

@EnableCaching
// session托管给redis
// @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 60)
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
        throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;

    }

    /**
     * 自定义redis序列化
     * 
     * @param connectionFactory
     * @return
     */
    @Bean(name = "redisTemplate2")
    public RedisTemplate<Object, Object> redisTemplate2(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);

        template.setValueSerializer(serializer);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
        throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

}

1.3 配置redis集群

配置文件

spring:
    redis:
      # redis.cluster
      cluster:
        nodes: 192.168.0.8:7004,192.168.0.8:7000,192.168.0.8:7002,192.168.0.8:7003,192.168.0.8:7005,192.168.0.8:7001

增加集群启动器

@Configuration
public class RedisClusterConfig {

    @Value("${spring.redis.cluster.nodes}")
    private String clusterNodes;

    @Bean
    public JedisCluster getJedisCluster() {
        String[] cNodes = clusterNodes.split(",");
        Set<HostAndPort> nodes = new HashSet<>();
        // 分割出集群节点
        for (String node : cNodes) {
            String[] hp = node.split(":");
            System.out.println("clusterNodes------------" + hp[0] + "------" + hp[1]);
            nodes.add(new HostAndPort(hp[0], Integer.parseInt(hp[1])));
        }
        // 创建集群对象
        return new JedisCluster(nodes, 5000);
    }

}

1.4 redisTemplate介绍

RedisTemplate是Spring Data Redis提供给用户的最高级的抽象客户端,用户可直接通过RedisTemplate进行多种操作,那么,我们先来看看RedisTemplate封装了哪些操作。下面这列表是RedisTemplate的继承关系和所有方法(已过滤重载方法,共有81个方法)

(1) 类继承关系

//RedisAccessor是RedisTemplate定义普通属性的基类,不直接使用
//RedisOperations是指定RedisTemplate实现的Redis connection操作的集合接口
//BeanClassLoaderAware是给其实现类是设置类加载器的接口
RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware

(2) 方法

//配置默认序列化与反序列化工具类
2.afterPropertiesSet
//根据参数执行相关operation操作,例如,事务
3.execute
//执行pipelining流水线相关操作
4.executePipelined
//执行指定connection连接的相关操作
5.executeWithStickyConnection
//执行session内的execute方法
6.executeSession
//创建RedisConnection代理类
7.createRedisConnectionProxy
//connection连接的预处理
8.preProcessConnection
//结果的后处理,默认什么都不做
9.postProcessResult
//是否向RedisCallback暴露本地连接
10.isExposeConnection
//设置是否向RedisCallback暴露本地连接
11.setExposeConnection
//12到26都是设置和获取相关序列化工具类
12.isEnableDefaultSerializer
13.setEnableDefaultSerializer
14.getDefaultSerializer
15.setDefaultSerializer
16.setKeySerializer
17.getKeySerializer
18.setValueSerializer
19.getValueSerializer
20.getHashKeySerializer
21.setHashKeySerializer
22.getHashValueSerializer
23.setHashValueSerializer
24.getStringSerializer
25.setStringSerializer
26.setScriptExecutor
//27到34为私有方法,不对外提供使用
27.rawKey
28.rawString
29.rawValue
30.rawKeys
31.deserializeKey
32.deserializeMixedResults
33.deserializeSet
34.convertTupleValues
//执行事务
35.exec
36.execRaw
//删除操作
37.delete
//接触链接
38.unlink
//查看是否含有指定key
39.hasKey
40.countExistingKeys
//设置过期时间
41.expire
42.expireAt
//转换成字节流并向channel发送message
43.convertAndSend
//获取过期时间
44.getExpire
//根据传入的正则表达式返回所有的key
46.keys
//取消指定key的过期时间
47.persist
//移动指定的key和index到数据库中
48.move
//从键空间随机获取一个key
49.randomKey
//将指定key改成目标key
50.rename
//key不存在时,将指定key改成目标key
51.renameIfAbsent
//设置存储在指定key的类型
52.type
//检索存储在key的值的序列化版本
53.dump
//执行Redis的restore的命令
54.restore
//标记事务阻塞的开始
55.multi
//丢弃所有在multi之后发出的命令
56.discard
//观察指定key在事务处理开始即multi之后的修改情况
57.watch
//刷新先前观察的所有key
58.unwatch
//为key元素排序
59.sort
//关闭客户端连接
60.killClient
//请求连接客户端的相关信息和统计数据
61.getClientList
//更改复制配置到新的master
62.slaveOf
//将本机更改为master
63.slaveOfNoOne
//64到79都是获取相对应的操作
64.opsForCluster
65.opsForGeo
66.boundGeoOps
67.boundHashOps
68.opsForHash
69.opsForHyperLogLog
70.opsForList
71.boundListOps
72.boundSetOps
73.opsForSet
74.opsForStream
75.boundStreamOps
76.boundValueOps
77.opsForValue
78.boundZSetOps
79.opsForZSet
//设置是否支持事务
80.setEnableTransactionSupport
//设置bean的类加载器
81.setBeanClassLoader

(3)功能介绍

spring-data-redis的提供了如下功能:

连接池自动管理,提供了一个高度封装的“RedisTemplate”类
进行了归类封装,将同一类型操作封装为operation接口

ValueOperations:简单K-V操作
SetOperations:set类型数据操作
ZSetOperations:zset类型数据操作
HashOperations:针对map类型的数据操作
ListOperations:针对list类型的数据操作

提供了对key的“bound”(绑定)便捷化操作API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key,即BoundKeyOperations:

BoundValueOperations
BoundSetOperations
BoundListOperations
BoundSetOperations
BoundHashOperations

将事务操作封装,有容器控制。
针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)

1.JdkSerializationRedisSerializer:POJO对象的存取场景,使用JDK本身序列化机制,将pojo类通过ObjectInputStream/ObjectOutputStream进行序列化操作,最终redis-server中将存储字节序列。是目前最常用的序列化策略。

2.StringRedisSerializer:Key或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是“newString(bytes,charset)”和“string.getBytes(charset)”的直接封装。是最轻量级和高效的策略。

3.JacksonJsonRedisSerializer:jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json格式存储在redis中,也可以将json格式的数据转换成pojo实例。因为jackson工具在序列化和反序列化时,需要明确指定Class类型,因此此策略封装起来稍微复杂。

4.OxmSerializer:提供了将javabean与xml之间的转换能力,目前可用的三方支持包括jaxb,apache-xmlbeans;redis存储的数据将是xml工具。不过使用此策略,编程将会有些难度,而且效率最低;不建议使用。【需要spring-oxm模块的支持】

如果你的数据需要被第三方工具解析,那么数据应该使用StringRedisSerializer而不是JdkSerializationRedisSerializer。

三.具体使用

(1) String类型:

插入操作

UserInfo markIfFirstSync = new UserInfo();
userRedisTemplate.opsForValue().set(CACHE_KEY, markIfFirstSync);

向redis中某个string类型的key下面插入一个对象。

批量插入操作
public Map<String, OrgRelationInfo> mappingRelationRefresh = new HashMap<>();
redisTemplate.opsForValue().multiSet(mappingRelationRefresh);

获取对象操作

userRedisTemplate.opsForValue().get(CACHE_NAME + CACHE_KEY_EXIST_PREFIX);

从redis中获取某个key下面的某个对象,如果不存在就返回null。

//批量获取对象操作
List sourceIdList = new ArrayList<>();
List orgMappingRelationList = redisTemplate.opsForValue().multiGet(sourceIdList);
//从redis中获取多个key下面的多个对象,返回一个List列表,但是即使对应的key下面没有值,这个value也会返回,不过是
//null,因此要判断是否List都为空,不能够用isEmpty直接判断,而应该一个一个的判断是否为空,才能判断整体为空。

(2) List类型

批量插入

//向redis的某个key下面的list列表里面插入一个list列表,不会去重。
List remainOrgNodes = new ArrayList<>();
redisTemplate.opsForList().leftPushAll(CACHE_KEY, remainOrgNodes);

批量取出

//从redis中取出某一个key下面的list列表, 0表示从列表的第0个元素开始取,-1表示直取到倒数第一个元素,也就是整个列表的所有元素都取出来。
List lastRemainOrgNodeList = redisTemplate.opsForList().range(CACHE_NAME + CACHE_REMAIN_KEY_PREFIX, 0, -1);

(3) Hash类型

批量插入

// 向redis中某个key下面插入key,hash的Map。
Map<Long, UserRelationInfo> value = new HashMap<>();
userHashRedisTemplate.opsForHash().putAll(KEY, value );

-单个删除

//从redis中某个key下面删除掉某个hashkey所在的value。
userHashRedisTemplate.opsForHash().delete(key, sourceOrgId);

单个获取

//从redis中某个key下面得到这个key对应的hashkey的value值。前一个key只能是String类型,hashKey可以声明为自己需要的类型。
userHashRedisTemplate.opsForHash().get(Key, hashKey);

批量获取

//从redis中得到某个key下面的所有的hashkey和hashvalue值。
Map<Object, Object> userOrgMap = userHashRedisTemplate.opsForHash().entries(getUserNodeCacheKey(syncUserNode.getSourceId()));

(4) Set类型

单个插入

//向redis的某个key下面的set列表里面插入一个元素,回去重,且无序。
userRoleSetRedisTemplate.opsForSet().add(KEY, cloudtOrgRoleInfo);

批量取出

//从redis的某个key下面得到set集合的所有元素,返回的也是一个Set集合。
cloudtOrgRoleSet = userRoleSetRedisTemplate.opsForSet().members(KEY);

单个删除

//从redis的某个key下面的set集合中删除掉一个元素。
userRoleSetRedisTemplate.opsForSet().remove( KEY, subDeleteOrgRoleUserArray[i]);

1.5 redis实战 缓存用户信息

实现Cookie传值,因为集群开发的话,session值会变,A机 和 B机 的session值一定改变。因此需要种入Cookie值保证用户登录之后不会出现,登录到A机 就只能在A机使用了。一但请求转到B机,就要重新登录了。

写入Cookie

  public static void writeLoginToken(HttpServletResponse response, String token) {
        Cookie ck = new Cookie(COOKIE_NAME, token);
        ck.setPath("/");
        ck.setHttpOnly(true);
        ck.setMaxAge(EXPIRE); // 保存时间
        log.info("write cookie name:{} , cookie vlaue: {}", ck.getName(), ck.getValue());
        response.addCookie(ck);
    }

读取Cookie

    public static String readLoginToken(HttpServletRequest request) {
        Cookie[] cks = request.getCookies();
        if (cks != null) {
            for (Cookie ck : cks) {
                log.info("read cookie name:{} , cookie value:{}", ck.getName(), ck.getValue());
                if (ck.getName().toString().equals(COOKIE_NAME)) {
                    return ck.getValue();
                }
            }
        }
        return null;
    }

删除Cookie(用于登出)

public static void delLoginToken(HttpServletResponse response, HttpServletRequest request) {
        Cookie[] cks = request.getCookies();
        if (cks != null) {
            for (Cookie ck : cks) {
                if (ck.getName().toString().equals(COOKIE_NAME)) {
                    ck.setPath("/");
                    ck.setMaxAge(0); // 0 = del
                    log.info("del cookie name : {} ,cookie vlaue: {}", ck.getName(), ck.getValue());
                    response.addCookie(ck);
                    return;
                }
            }
        }
    }

2.2 得到Cookie值之后只要把Cookie的值放入到redis里面,设置一样的时间值,就实现了集群登录。

 // String token = UUID.randomUUID().toString();
        String token = session.getId(); // 获取登录 token
        Integer expire = CookieUtil.EXPIRE; // 设置时间
        // 设置 token 至 redis
        redisTemplate.opsForValue().set(
            // String TOKEN_PREFIX = "sample_token_%s";
            String.format(RedisConfig.TOKEN_PREFIX, token) // key
            , sellerInfo.getId().toString() // 关键字 value
            , expire, // 时间
            TimeUnit.SECONDS); // 格式

        // 设置 token 至 cookie
        CookieUtil.writeLoginToken(httpServletResponse, token);

2.3结合使用

    @Autowired
    private StringRedisTemplate redisTemplate;

     @RequestMapping("/do-login")
    @ResponseBody
    public String login(@RequestParam("userName") String userName, @RequestParam("password") String password,
        HttpServletResponse httpServletResponse, HttpSession session) {
        // 数据库匹配
        User sellerInfo = userService.findByUserName(userName); // 通过关键字 查询用户
        if (sellerInfo == null) { // 判断用户是否存在
            return "用户不存在";
        }
        // String token = UUID.randomUUID().toString();
        String token = session.getId(); // 获取登录 token
        Integer expire = CookieUtil.EXPIRE; // 设置时间
        // 设置 token 至 redis
        redisTemplate.opsForValue().set(
            // String TOKEN_PREFIX = "sample_token_%s";
            String.format(RedisConfig.TOKEN_PREFIX, token) // key
            , sellerInfo.getId().toString() // 关键字 value
            , expire, // 时间
            TimeUnit.SECONDS); // 格式

        // 设置 token 至 cookie
        CookieUtil.writeLoginToken(httpServletResponse, token);
        return "success";
    }

2.4登出

    @RequestMapping("/do-logout")
    public String logout(HttpServletRequest request, HttpServletResponse response, Map<String, Object> map) {
        // cookie查询
        String token = CookieUtil.readLoginToken(request);
        if (!StringUtils.isEmpty(token)) {
            // 清除 redis
            redisTemplate.opsForValue().getOperations().delete(String.format(RedisConfig.TOKEN_PREFIX, token));

            // del cookie
            CookieUtil.delLoginToken(response, request);
        }
        return "login";

    }

用户信息存储,引入数据库,故整合下jpa

整合jpa

配置文件增加jpa

spring:
    ## jsp
    mvc:
      view: 
        prefix: /WEB-INF/jsp/
        suffix: .jsp
     
    datasource:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3307/springboot?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
      hikari: # springboot 2.0 整合了hikari ,据说这是目前性能最好的java数据库连接池
        username: root
        password: 
    jpa:
      show-sql: true
      database: mysql
      hibernate:
        ddl-auto: update

pom依赖增加jpa

<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		
		<dependency>
		    <groupId>mysql</groupId>
		    <artifactId>mysql-connector-java</artifactId>
		    <scope>runtime</scope>
		</dependency>
		<dependency>
		    <groupId>org.projectlombok</groupId>
		    <artifactId>lombok</artifactId>
		    <optional>true</optional>
		</dependency>
<!-- jsp -->
		<dependency>
		    <groupId>javax.servlet</groupId>
		    <artifactId>jstl</artifactId>
		</dependency>
		<dependency>
		    <groupId>org.apache.tomcat.embed</groupId>
		    <artifactId>tomcat-embed-jasper</artifactId>
		    <!--<scope>provided</scope>-->
		</dependency>

model源码

@Entity
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String userName;
    private String passWord;
    private Integer age;

}

持久化源码

public interface UserDao extends JpaRepository<User, Integer> {

    User findByUserName(String userName);

    User findByIdIs(Integer id);

}

启动测试下
在这里插入图片描述
输入用户密码登录后查看redis数据
在这里插入图片描述

源码地址

https://gitee.com/xiaolaifeng/sample.springboot/tree/master/springboot-teach9

2. 整合mongo

2.1 引入pom依赖

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
		    <groupId>org.projectlombok</groupId>
		    <artifactId>lombok</artifactId>
		    <optional>true</optional>
		</dependency>

2.2 定义model


@Data
@Document(collection = "user")
public class User implements Serializable {
    @Id
    private Long id;
    private String userName;
    private String passWord;
    private Integer age;
    private Date createTime;

}

2.3 编写持久化类

UserDao.java UserDaoImpl.java

public interface UserDao {

    User findByUserName(String userName);

    User save(User user);

    void update(User user);

    List<User> findAll();

    void delete(Integer id);
}
@Component
public class UserDaoImpl implements UserDao {

    @Autowired
    private MongoTemplate mongoTemplate;

    /* 
     * 根据姓名查询用户
     */
    @Override
    public User findByUserName(String userName) {
        // 查询的条件
        Query query = new Query(Criteria.where("userName").is(userName));

        return mongoTemplate.findOne(query, User.class);
    }

    /**
     * 新增信息
     * 
     * @param User
     */
    @Override
    public User save(User User) {
        return mongoTemplate.save(User);
    }

    /**
     * 修改信息
     * 
     * @param User
     */
    @Override
    public void update(User User) {
        // 修改的条件
        Query query = new Query(Criteria.where("id").is(User.getId()));

        // 修改的内容
        Update update = new Update();
        update.set("name", User.getUserName());

        mongoTemplate.updateFirst(query, update, User.class);
    }

    /**
     * 查询所有信息
     * 
     * @return
     */
    @Override
    public List<User> findAll() {
        return mongoTemplate.findAll(User.class);
    }

    /**
     * 根据id查询所有信息
     * 
     * @param id
     */
    @Override
    public void delete(Integer id) {
        User byId = mongoTemplate.findById(1, User.class);
        mongoTemplate.remove(byId);
    }
}

2.4 编写test

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootTeach9ApplicationTests {

    @Autowired
    private UserDao userDao;

    @Test
    public void contextLoads() {}

    @Test
    public void addUser() {
        User user = new User();
        user.setId(1l);
        user.setUserName("李四");
        user.setPassWord("111111");
        user = userDao.save(user);
        System.out.println(user.getId());
    }

    /**
     * 查询所有信息
     */
    @Test
    public void findAll() {
        List<User> all = userDao.findAll();
        System.out.println(all.size());
    }

    /**
     * 修改信息
     */
    @Test
    public void update() {
        User user = new User();
        user.setId(1l);
        user.setUserName("张三2");
        userDao.update(user);
    }

    /**
     * 删除信息
     */
    @Test
    public void delete() {
        userDao.delete(3);
    }
}

源码地址 分支mongo

https://gitee.com/xiaolaifeng/sample.springboot/tree/master/springboot-teach9

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浮华落定

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值