Redis拾遗(六)——基于springboot操作Redis基础数据类型

本篇博客开始进入到Redis的实例部分,基于springboot列举一些Redis的使用场景,同时针对不同的应用场景顺便复习一下springboot的一些操作。

准备工作

构建一个springboot-redis的项目,并在这个项目下构建三个子模块

整体结构如下所示:

Redis拾遗(六)——基于springboot操作Redis基础数据类型_第1张图片

各个职责如下

springboot-redis-api——一些与H5交互的请求与返回体的定义
springboot-redis-model——持久层与数据访问层的一些实体与mapper
springboot-redis-server——主要业务的模块

主pom.xml


<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">

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.2.2.RELEASEversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <groupId>com.learngroupId>
    <artifactId>spring-boot-redisartifactId>
    <packaging>pompackaging>
    <version>1.0-SNAPSHOTversion>

    <name>springboot中操作redis的相关应用场景name>
    <url>https://blog.csdn.net/liman65727url>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <java.version>1.8java.version>
        <maven.compiler.source>${java.version}maven.compiler.source>
        <maven.compiler.target>${java.version}maven.compiler.target>

        <spring-boot.version>2.2.2.RELEASEspring-boot.version>
        <slf4j.version>1.7.13slf4j.version>
        <log4j.version>1.2.17log4j.version>
        <mysql.version>5.1.37mysql.version>
        <mybatis-spring-boot.version>1.1.1mybatis-spring-boot.version>
        <mybatis-pagehelper.version>5.1.4mybatis-pagehelper.version>
        <druid.version>1.0.16druid.version>
        <javax-validation.version>1.1.0.Finaljavax-validation.version>
        <hibernate-validator.version>6.0.18.Finalhibernate-validator.version>
        <guava.version>19.0guava.version>
        <joda-time.version>2.9.2joda-time.version>
        <retrofit.version>2.3.0retrofit.version>

        <cglib.version>3.1cglib.version>
        <resteasy.version>3.0.14.Finalresteasy.version>

        <commons-beanutils.version>1.9.2commons-beanutils.version>
        <redisson.version>3.8.2redisson.version>

        <javamail.version>1.4.7javamail.version>
        <lombok.version>1.16.10lombok.version>
    properties>

    <dependencies>
        
        
            
            
            
        

        
        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-apiartifactId>
            <version>${slf4j.version}version>
        dependency>

        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-log4j12artifactId>
            <version>${slf4j.version}version>
        dependency>

        <dependency>
            <groupId>log4jgroupId>
            <artifactId>log4jartifactId>
            <version>${log4j.version}version>
        dependency>

        
        <dependency>
            <groupId>commons-beanutilsgroupId>
            <artifactId>commons-beanutilsartifactId>
            <version>${commons-beanutils.version}version>
        dependency>

        
        <dependency>
            <groupId>com.squareup.retrofit2groupId>
            <artifactId>retrofitartifactId>
            <version>${retrofit.version}version>
        dependency>

        <dependency>
            <groupId>com.squareup.retrofit2groupId>
            <artifactId>converter-jacksonartifactId>
            <version>${retrofit.version}version>
        dependency>

        
        <dependency>
            <groupId>com.google.guavagroupId>
            <artifactId>guavaartifactId>
            <version>${guava.version}version>
        dependency>

        
        <dependency>
            <groupId>com.google.code.gsongroupId>
            <artifactId>gsonartifactId>
            <version>2.6.1version>
        dependency>

        
        <dependency>
            <groupId>joda-timegroupId>
            <artifactId>joda-timeartifactId>
            <version>${joda-time.version}version>
        dependency>

        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>${mysql.version}version>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>${druid.version}version>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
            <version>${spring-boot.version}version>
            <exclusions>
                <exclusion>
                    <groupId>ch.qos.logbackgroupId>
                    <artifactId>logback-classicartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.slf4jgroupId>
                    <artifactId>log4j-over-slf4jartifactId>
                exclusion>
            exclusions>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-tomcatartifactId>
             
        dependency>

        <dependency>
            <groupId>org.apache.tomcat.embedgroupId>
            <artifactId>tomcat-embed-jasperartifactId>
            
        dependency>

        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>jstlartifactId>
        dependency>

        <dependency>
            <groupId>cglibgroupId>
            <artifactId>cglibartifactId>
            <version>${cglib.version}version>
            <exclusions>
                <exclusion>
                    <artifactId>asmartifactId>
                    <groupId>org.ow2.asmgroupId>
                exclusion>
            exclusions>
        dependency>

        
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjrtartifactId>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
            <version>${spring-boot.version}version>
        dependency>
        

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-amqpartifactId>
            <version>${spring-boot.version}version>
        dependency>

        
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-quartzartifactId>
            <version>1.3.2version>
            <exclusions>
                <exclusion>
                    <groupId>org.opensymphony.quartzgroupId>
                    <artifactId>quartzartifactId>
                exclusion>
            exclusions>
        dependency>

        
        <dependency>
            <groupId>org.redissongroupId>
            <artifactId>redissonartifactId>
            <version>${redisson.version}version>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-freemarkerartifactId>
        dependency>

        
        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>4.6.10version>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-mailartifactId>
            <version>1.5.7.RELEASEversion>
        dependency>

        
        

        

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>${lombok.version}version>
        dependency>

        
        <dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelperartifactId>
            <version>${mybatis-pagehelper.version}version>
        dependency>

        
        <dependency>
            <groupId>javax.validationgroupId>
            <artifactId>validation-apiartifactId>
            <version>${javax-validation.version}version>
        dependency>
        <dependency>
            <groupId>org.hibernategroupId>
            <artifactId>hibernate-validatorartifactId>
            <version>${hibernate-validator.version}version>
        dependency>
    dependencies>

    
    <repositories>
        <repository>
            <id>publicid>
            <name>aliyun nexusname>
            <url>http://maven.aliyun.com/nexus/content/groups/public/url>
            <releases>
                <enabled>trueenabled>
            releases>
        repository>
    repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>publicid>
            <name>aliyun nexusname>
            <url>http://maven.aliyun.com/nexus/content/groups/public/url>
            <releases>
                <enabled>trueenabled>
            releases>
            <snapshots>
                <enabled>falseenabled>
            snapshots>
        pluginRepository>
    pluginRepositories>
	
    <modules>
        <module>springboot-redis-apimodule>
        <module>springboot-redis-modelmodule>
        <module>springboot-redis-servicemodule>
    modules>
project>

server中的pom

    <dependencies>
        <dependency>
            <groupId>com.learn.springboot-redis-modelgroupId>
            <artifactId>springboot-redis-modelartifactId>
            <version>${project.parent.version}version>
        dependency>
    dependencies>

所有的依赖均交给父pom去管理了,这里只需要引入我们需要的内部module就可以了,server需要依赖model的相关内容

module中的pom

    <dependencies>
        <dependency>
            <groupId>com.learn.springboot-redis-apigroupId>
            <artifactId>springboot-redis-apiartifactId>
            <version>${project.parent.version}version>
        dependency>

        
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>${mybatis-spring-boot.version}version>
        dependency>
    dependencies>

引入自己需要的mybatis

api中的pom

    <dependencies>
        
        <dependency>
            <groupId>javax.validationgroupId>
            <artifactId>validation-apiartifactId>
            <version>${javax-validation.version}version>
        dependency>
        <dependency>
            <groupId>org.hibernategroupId>
            <artifactId>hibernate-validatorartifactId>
            <version>${hibernate-validator.version}version>
        dependency>

        
        <dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelperartifactId>
            <version>${mybatis-pagehelper.version}version>
        dependency>

        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>${lombok.version}version>
        dependency>
    dependencies>

一些校验和分页的依赖。

SQL脚本

执行如下SQL脚本,完成数据库的准备

CREATE DATABASE db_springboot_redis;
USE db_springboot_redis;

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for item
-- ----------------------------
DROP TABLE IF EXISTS `item`;
CREATE TABLE `item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(255) DEFAULT NULL COMMENT '商品编号',
  `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '商品名称',
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_code` (`code`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10012 DEFAULT CHARSET=utf8 COMMENT='商品信息表';


-- ----------------------------
-- Table structure for notice
-- ----------------------------
DROP TABLE IF EXISTS `notice`;
CREATE TABLE `notice` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '通告标题',
  `content` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '内容',
  `is_active` tinyint(4) DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COMMENT='通告';


-- ----------------------------
-- Table structure for phone_fare
-- ----------------------------
DROP TABLE IF EXISTS `phone_fare`;
CREATE TABLE `phone_fare` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `phone` varchar(50) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '手机号码',
  `fare` decimal(10,2) DEFAULT NULL COMMENT '充值金额',
  `is_active` tinyint(4) DEFAULT '1' COMMENT '是否有效(1=是;0=否)',
  PRIMARY KEY (`id`),
  KEY `idx_phone` (`phone`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8 COMMENT='手机充值记录';


-- ----------------------------
-- Table structure for problem
-- ----------------------------
DROP TABLE IF EXISTS `problem`;
CREATE TABLE `problem` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(150) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '问题标题',
  `choice_a` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '选项A',
  `choice_b` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '选项B',
  `is_active` tinyint(4) DEFAULT '1' COMMENT '是否有效(1=是;0=否)',
  `order_by` tinyint(4) DEFAULT '0' COMMENT '排序',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_title` (`title`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='问题库列表';


-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '商品名称',
  `user_id` int(11) NOT NULL COMMENT '所属用户id',
  `scan_total` int(255) DEFAULT NULL COMMENT '浏览量',
  `is_active` tinyint(255) DEFAULT '1' COMMENT '是否有效',
  PRIMARY KEY (`id`),
  KEY `indx_scan_total` (`scan_total`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 COMMENT='商户商品表';


-- ----------------------------
-- Table structure for red_detail
-- ----------------------------
DROP TABLE IF EXISTS `red_detail`;
CREATE TABLE `red_detail` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `record_id` int(11) NOT NULL COMMENT '红包记录id',
  `amount` decimal(8,2) DEFAULT NULL COMMENT '每个小红包的金额(单位为分)',
  `is_active` tinyint(4) DEFAULT '1',
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=193 DEFAULT CHARSET=utf8 COMMENT='红包明细金额';

-- ----------------------------
-- Table structure for red_record
-- ----------------------------
DROP TABLE IF EXISTS `red_record`;
CREATE TABLE `red_record` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '用户id',
  `red_packet` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '红包全局唯一标识串',
  `total` int(11) NOT NULL COMMENT '人数',
  `amount` decimal(10,2) DEFAULT NULL COMMENT '总金额(单位为分)',
  `is_active` tinyint(4) DEFAULT '1',
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8 COMMENT='发红包记录';


-- ----------------------------
-- Table structure for red_rob_record
-- ----------------------------
DROP TABLE IF EXISTS `red_rob_record`;
CREATE TABLE `red_rob_record` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL COMMENT '用户账号',
  `red_packet` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '红包标识串',
  `amount` decimal(8,2) DEFAULT NULL COMMENT '红包金额(单位为分)',
  `rob_time` datetime DEFAULT NULL COMMENT '时间',
  `is_active` tinyint(4) DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=178 DEFAULT CHARSET=utf8 COMMENT='抢红包记录';

-- ----------------------------
-- Table structure for sys_config
-- ----------------------------
DROP TABLE IF EXISTS `sys_config`;
CREATE TABLE `sys_config` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '字典类型',
  `name` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '字典名称',
  `code` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '选项编码',
  `value` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '选项取值',
  `order_by` int(11) DEFAULT '1' COMMENT '排序',
  `is_active` tinyint(4) DEFAULT '1' COMMENT '是否有效(1=是;0=否)',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_type_code` (`type`,`code`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT='字典配置表';


-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '姓名',
  `email` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_email` (`email`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=52 DEFAULT CHARSET=utf8 COMMENT='用户表';

项目集成相关框架

springboot的自动注入机制,使得我们集成第三方框架变得非常简单。

springboot数据源的配置

各位可以按照自己的喜好,集成druid,springboot自带的数据源等等等,这里我不打算采用springboot默认集成的数据源,也不打算采用druid第三方数据源组件,打算还是采用一个比较老的集成方式

建立一个spring-jdbc.xml文件,在里头完成数据源的配置


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       " >

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close" primary="true" >
        
        <property name="url" value="${datasource.url}" />
        <property name="username" value="${datasource.username}" />
        <property name="password" value="${datasource.password}" />

        
        <property name="initialSize" value="10" />
        <property name="minIdle" value="10" />
        <property name="maxActive" value="20" />

        
        <property name="maxWait" value="60000" />

        
        <property name="timeBetweenEvictionRunsMillis" value="60000" />

        
        <property name="minEvictableIdleTimeMillis" value="300000" />
        <property name="validationQuery" value="SELECT 1 " />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />

        
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />

        
        <property name="filters" value="stat" />
    bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    bean>
beans>

mybatis的配置文件

mybatis-config.xml



<configuration>

    <settings>
        
        <setting name="cacheEnabled" value="true"/>
        
        <setting name="defaultStatementTimeout" value="3000"/>
        
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        
        <setting name="useGeneratedKeys" value="true"/>
        
        <setting name="logImpl" value="stdout_logging" />
    settings>
configuration>

properties配置文件

#profile多环境配置 - 默认即为本配置文件
#spring.profiles.active=prod
#spring.profiles.active=test

#应用端口-应用上下文路径
server.port=8099
server.servlet.context-path=/redis


#前端模板引擎
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.suffix=.html
spring.freemarker.request-context-attribute=request
spring.freemarker.template-loader-path=classpath:/templates

#限定前端上传的文件大小
spring.servlet.multipart.max-request-size=20MB
spring.servlet.multipart.max-file-size=2MB

#日志级别
logging.level.org.springframework = INFO
logging.level.com.fasterxml.jackson = INFO
logging.level.com.learn.springboot = DEBUG

#json序列化配置
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

spring.datasource.initialization-mode=never
spring.jmx.enabled=false

#数据源配置
datasource.url=jdbc:mysql://127.0.0.1:3306/db_springboot_redis?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
datasource.username=root
datasource.password=root

#mybatis
mybatis.config-location=classpath:mybatis-config.xml
mybatis.checkConfigLocation = true
mybatis.mapper-locations=classpath:mappers/*.xml

#redis 单机配置
spring.redis.host=192.168.72.128
spring.redis.port=6379
spring.redis.password=
spring.redis.jedis.pool.min-idle=100
spring.redis.jedis.pool.max-idle=300
spring.redis.jedis.pool.max-active=500

#集群配置
#spring.redis.cluster.nodes=127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382

#通用配置
server.tomcat.additional-tld-skip-patterns=jaxb-api.jar,jaxb-core.jar

#spring java mail 发送邮件服务配置
spring.mail.host=smtp.qq.com
spring.mail.username=
spring.mail.password=
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
mail.send.from=

由于springboot自动注入的机制,我们只需要在properties文件中配置spring.redis的相关属性即可,集成Redis变成异常简单。

Redis相关的配置类

/**
 * autor:liman
 * createtime:2020/7/19
 * comment:
 */
@Configuration
public class RedisConfg {

    //springboot会为我们自动注入该工厂类
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    //配置RedisTemplate中的一些序列化策略
    @Bean
    public RedisTemplate redisTemplate(){
        RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置序列化策略
        redisTemplate.setKeySerializer(new StringRedisSerializer());//设置redis中key值的序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//设置redis中value值的序列化
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());//设置haskKey值的序列化

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    //配置StringRedisTemplate
    @Bean
    public StringRedisTemplate stringRedisTemplate(){
        StringRedisTemplate stringRedisTemplate=new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
        return stringRedisTemplate;
    }

}

主类中加入相关注解

/**
 * autor:liman
 * createtime:2020/7/26
 * comment:
 */
@SpringBootApplication
@ImportResource(value = {"classpath:spring/spring-jdbc.xml"}) //引入spring-jdbc.xml的配置
@MapperScan(basePackages = "com.learn.springboot.redis.model.mapper")//扫描mapper文件的路径配置
public class SpringBootRedisApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootRedisApplication.class,args);
    }
}

String类型

这里我们以操作item实体来进行举例,Item实体可以看成是一个简单的商品实体,Item表对应简单的商品数据表。这里简单介绍商品实体的存取

StringRedis的操作逻辑

package com.learn.springboot.redis.service.service.redis;

import com.learn.springboot.redis.service.constants.RedisKeyConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * autor:liman
 * createtime:2020/7/26
 * comment:操作string的Redisservice
 */
@Service
@Slf4j
public class StringRedisService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private ValueOperations getOperation(){
        return stringRedisTemplate.opsForValue();
    }

    /**
     * 设置key-value
     * @param key
     * @param value
     * @throws Exception
     */
    public void put(final String key,final String value) throws Exception{
        getOperation().set(RedisKeyConstants.RedisStringPrefix+key,value);
    }

    /**
     * 获取key-value
     * @param key
     * @return
     * @throws Exception
     */
    public Object get(final String key) throws Exception{
        return getOperation().get(RedisKeyConstants.RedisStringPrefix+key);
    }

    /**
     * 判断key-value是否存在
     * @param key
     * @return
     * @throws Exception
     */
    public Boolean exist(final String key) throws Exception{
        return stringRedisTemplate.hasKey(RedisKeyConstants.RedisStringPrefix+key);
    }

    /**
     * 设置指定的key-value过期时间
     * @param key
     * @param expireSeconds
     * @throws Exception
     */
    public void expire(final String key,final Long expireSeconds) throws Exception{
        stringRedisTemplate.expire(key,expireSeconds, TimeUnit.SECONDS);
    }
}

Item的业务操作逻辑

/**
 * autor:liman
 * createtime:2020/7/26
 * comment:
 */
@Service
@Slf4j
public class StringService {

    private static final String KEY_PREFIX = "ITEM_";

    @Autowired
    private ItemMapper itemMapper;

    @Autowired
    private StringRedisService stringRedisService;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 添加商品
     * @param item
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer addItem(Item item) throws Exception {
        //先保存的数据库再保存的redis
        item.setCreateTime(new Date());
        item.setId(null);
        itemMapper.insertSelective(item);
        Integer id = item.getId();
		//如果保存成功,这需要更新缓存
        if(id>0){
            stringRedisService.put(KEY_PREFIX+id.toString(),objectMapper.writeValueAsString(item));
        }
        return id;
    }

    /**
     * 获取商品
     * @param id
     * @return
     * @throws Exception
     */
    public Item getItem(Integer id) throws Exception {
        Item item = null;
        if(id!=null){
            //先取缓存中的数据
            if(stringRedisService.exist(KEY_PREFIX+id.toString())){
                String result=stringRedisService.get(id.toString()).toString();
                log.info("---string数据类型,从缓存中取出来的value:{}---",result);
                if(StrUtil.isNotBlank(result)){
                    item = objectMapper.readValue(result,Item.class);
                }
            }else{//如果缓存没有,则先从数据库区,取出之后并更新Redis
                log.info("---string数据类型,从数据库查询:id={}---",id);
                item=itemMapper.selectByPrimaryKey(id);
                if (item!=null){
                    stringRedisService.put(KEY_PREFIX+id.toString(),objectMapper.writeValueAsString(item));
                }
            }
        }
        return item;
    }
}

Controller层的逻辑

/**
 * autor:liman
 * createtime:2020/7/26
 * comment:
 */
@RestController
@Slf4j
@RequestMapping("string")
public class StringController {

    @Autowired
    private StringService stringService;

    /**
     * 添加商品
     * @param item
     * @param bindingResult
     * @return
     */
    @RequestMapping(value="put",method=RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_VALUE)
    public BaseResponse put(@RequestBody @Validated Item item,BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return new BaseResponse(StatusCode.InvalidParams);
        }
        BaseResponse response = new BaseResponse(StatusCode.Success);
        try{
            log.info("--添加商品信息:{}--",item);
            stringService.addItem(item);
        }catch (Exception e){
            log.error("--字符串String实战-商品详情存储-添加-发生异常--:",e.fillInStackTrace());
            response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
    }

    @RequestMapping(value = "get",method = RequestMethod.GET)
    public BaseResponse get(@RequestParam Integer prodId){
        BaseResponse response=new BaseResponse(StatusCode.Success);
        try {
            response.setData(stringService.getItem(prodId));
        }catch (Exception e){
            log.error("--Redis字符串String实战-商品详情存储-获取-发生异常:",e.fillInStackTrace());
            response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
    }
}

List类型

List类型与String类型的操作大同小异,这里不再贴出全部代码,但是之前介绍过,list可以实现异步消息通知。

操作Redis中List数据类型的逻辑

/**
 * autor:liman
 * createtime:2020/7/26
 * comment:Redis 关于list 的操作
 */
@Slf4j
@Service
public class ListRedisService {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 将product 放入到Redis list中
     * @param product
     */
    public void pushRedisObject(final Product product){
        ListOperations<String,Product> listOperations = redisTemplate.opsForList();
        listOperations.leftPush(RedisKeyConstants.RedisListPrefix+product.getUserId(),product);
    }

    /**
     * pop获取list中的object
     * @param productKey
     * @return
     */
    public Product popRedisObject(final String productKey){
        ListOperations<String,Product> listOperations = redisTemplate.opsForList();
        Product product = listOperations.rightPop(productKey);
        return product;
    }

    /**
     * 获取Redis中存入的productList
     * @param productKey
     * @param reverse 是否倒序
     * @return
     */
    public List<Product> getRedisListObject(final String productKey,boolean reverse){
        ListOperations<String,Product> listOperations = redisTemplate.opsForList();
        List<Product> productList =Lists.newLinkedList();
        productList = listOperations.range(productKey,0,listOperations.size(productKey));
        if(!reverse){
            return productList;
        }else{
            Collections.reverse(productList);
            return productList;
        }
    }

    /**
     * 将一个数组批量压入到Redis中的list中。
     * @param productKey
     * @param productList
     * @return
     */
    public Long pushListIntoRedis(final String productKey,final List<Product> productList){
        ListOperations<String,Product> listOperations = redisTemplate.opsForList();
        Long aLong = listOperations.leftPushAll(productKey, productList);
        return aLong;
    }
}

操作Redis中List类型的业务逻辑

/**
 * autor:liman
 * createtime:2020/7/26
 * comment:List的业务Service
 */
@Slf4j
@Service
public class ListService {

    @Autowired
    private ListRedisService listRedisService;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private NoticeMapper noticeMapper;

    /**
     * 添加商品
     * @param product
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer addProduct(Product product){
        if(product!=null){
            product.setId(null);
            productMapper.insertSelective(product);
            Integer id = product.getId();
            if(id>0){
                listRedisService.pushRedisObject(product);
            }
            return id;
        }
        return -1;
    }

    public List<Product> getHistoryProducts(final Integer userId){
        log.info("=====开始查询productList:{}=====",userId);
        List<Product> productList = Lists.newLinkedList();
        final String productKey = RedisKeyConstants.RedisListPrefix+userId;
        productList = listRedisService.getRedisListObject(productKey, false);
        if(1>productList.size()){//如果缓存中没有则直接去数据库中取数据,并更新缓存
            productList=productMapper.listProductsByUId(userId);
            listRedisService.pushListIntoRedis(productKey,productList);//这种是List中只有一个元素,这个元素是一个list
        }
        log.info("=====查询的productList结果为:{}=====",productList);
        return productList;
    }
}

异步的批量逻辑

1、在ListController(完整代码没有贴出)中加入一个增加Notice的逻辑

//平台发送通知给到各位商户
@RequestMapping(value = "/notice/put",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_VALUE)
public BaseResponse putNotice(@RequestBody @Validated Notice notice, BindingResult result){
    BaseResponse response=new BaseResponse(StatusCode.Success);
    try {
        log.info("--平台发送通知给到各位商户:{}",notice);

        listService.pushNotice(notice);
    }catch (Exception e){
        response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
    }
    return response;
}

2、ListService中加入存入Notice的逻辑

/**
 * 将需要发送的数据放入到redis队列缓存
 * @param notice
 */
@Transactional(rollbackFor = Exception.class)
public void pushNotice(Notice notice){
    if(notice!=null){
        notice.setId(null);
        noticeMapper.insertSelective(notice);
        final Integer noticeId = notice.getId();

        if(noticeId>0){//这里就将notice存入到Redis中
            listRedisService.pushNoticeInRedis(notice);
        }
    }
}

将Notice存入到了Redis中之后,我们需要通过一个异步的批量操作,完成Redis中队列消息的处理,关于springboot启动批量任务,可以参考之前的博客 springboot开启定时任务。

3、开启定时任务的逻辑

/**
 * autor:liman
 * createtime:2020/7/26
 * comment:
 */
@Slf4j
@Component
@EnableScheduling
public class NoticeListenerScheduler {

    private static final String noticeListenerKey = RedisKeyConstants.RedisListNoticeKey;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private EmailService emailService;

    /**
    	批量任务的开启逻辑,循环消费list中的Notice实例
    */
    @Scheduled(cron = "0/60 * * * * ?")
    public void noticeListenerScheduler(){
        log.info("=====定时任务开启,读取queue中的数据,并发送数据=====");
        ListOperations<String,Notice> listOperations = redisTemplate.opsForList();
        Notice notice = listOperations.rightPop(noticeListenerKey);
        while(null!=notice){
            log.info("======开始给指定商户:{}发送邮件=====");

            this.noticeUser(notice);
            notice = listOperations.rightPop(noticeListenerKey);
        }
    }
    
	//发送通知给到不同的商户异步线程池
	@Async("threadPoolTaskExecutor")
	public void noticeUser(Notice notice){
		if (notice!=null){
			List<User> list=userMapper.selectList();

			//TODO:写法一-java8 stream api触发
			/*if (list!=null && !list.isEmpty()){
				list.forEach(user -> emailService.emailUserNotice(notice,user));
			}*/


			//TODO:写法二-线程池/多线程触发
			try {
				if (list!=null && !list.isEmpty()){
					ExecutorService executorService=Executors.newFixedThreadPool(4);
					List<NoticeThread> threads= Lists.newLinkedList();

					list.forEach(user -> {
						threads.add(new NoticeThread(user,notice,emailService));
					});

					executorService.invokeAll(threads);
				}
			}catch (Exception e){
				log.error("近实时的定时任务检测-发送通知给到不同的商户-法二-线程池/多线程触发-发生异常:",e.fillInStackTrace());
			}
		}
	}
}

4、加入线程池的配置

/**
 * autor:liman
 * createtime:2020/7/26
 * comment:
 */
@Configuration
@Slf4j
public class ThreadPoolConfig {
    @Bean("threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(8);
        executor.setKeepAliveSeconds(10);
        executor.setQueueCapacity(8);

        //设置的拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

别忘了,主类中需要加上异步和批量的注解

/**
 * autor:liman
 * createtime:2020/7/26
 * comment:
 */
@SpringBootApplication
@ImportResource(value = {"classpath:spring/spring-jdbc.xml"})
@MapperScan(basePackages = "com.learn.springboot.redis.model.mapper")
@EnableScheduling
@EnableAsync
public class SpringBootRedisApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootRedisApplication.class,args);
    }
}

总结

本篇博客由于篇幅所限,主要介绍了项目创建于集成的工作,目前只能介绍到String与List类型的基本操作。

你可能感兴趣的:(分布式)