第3章 SpringBoot+MyBatisPlus+Redis案例实战

3.1 建表

CREATE TABLE `t_user` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `username` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '用户名',
    `password` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '密码',
    `sex` TINYINT(4) NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',
    `deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',
    `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8 COMMENT='用户表';

3.2 创建SpringBoot项目

3.2.1 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.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.10.RELEASE</version>
        <relativePath/>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>redis-01-crud</artifactId>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--SpringBoot通用依赖模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>2.0.8</version>
        </dependency>

        <!--SpringBoot与Redis整合依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--springCache-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!--springCache连接池依赖包-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!-- jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <!--Mysql数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>

        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.5</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--persistence-->
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>persistence-api</artifactId>
            <version>1.0.2</version>
        </dependency>
    </dependencies>

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

3.2.2 application.yaml

server:
  port: 8001

spring:
  application:
    name: guli_user

  datasource:
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false

  redis:
    host: 192.168.123.133
    port: 6379
    database: 0
    lettuce:
      pool:
        max-active: 8  #连接池最大连接数(使用负值表示没有限制) 默认 8
        max-wait: -1ms  #连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1,记得加入单位ms,不然idea报红色
        max-idle: 8  #连接池中的最大空闲连接 默认 8
        min-idle: 0  #连接池中的最小空闲连接 默认 0
        
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志
  global-config:
    db-config:
      logic-delete-field: 1 # 逻辑删除标志
      logic-not-delete-value: 0

3.2.3 MyBatisPlus生成代码

3.2.4 配置类

RedisConfig

@Configuration
@Slf4j
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();

        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        //设置key序列化方式string
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置value的序列化方式为json
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

Knife4jConfig

@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfig {

    @Bean
    public Docket adminApiConfig(){
        List<Parameter> pars = new ArrayList<>();
        ParameterBuilder tokenPar = new ParameterBuilder();
        tokenPar.name("token")
                .description("用户token")
                .defaultValue("")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tokenPar.build());
        //添加head参数end

        Docket adminApi = new Docket(DocumentationType.SWAGGER_2)
                .groupName("adminApi")
                .apiInfo(adminApiInfo())
                .select()
                //只显示admin路径下的页面
                .apis(RequestHandlerSelectors.basePackage("com.fystart.redis")) //注意包名
                .paths(PathSelectors.regex("/admin/.*")) //注意去掉
                .build()
                .globalOperationParameters(pars);
        return adminApi;
    }

    private ApiInfo adminApiInfo(){

        return new ApiInfoBuilder()
                .title("后台管理系统-API文档")
                .description("本文档描述了后台管理系统微服务接口定义")
                .version("1.0")
                .contact(new Contact("zhangsan", "http://baidu.com", "zhangsan@qq.com"))
                .build();
    }
}

3.2.5 crud业务代码

UserController

@Api(value = "用户User接口")
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    @ApiOperation("数据库新增5条记录")
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public void addUser() {
        for (int i = 0; i < 5; i++) {
            User user = new User();
            user.setUsername("zzyy" + i);
            user.setPassword(IdUtil.simpleUUID().substring(0, 6));
            //[0,2)
            user.setSex(new Random().nextInt(2));
            userService.addUser(user);
        }
    }

    @ApiOperation("删除一条记录")
    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
    public void deleteUser(@PathVariable Integer id) {
        userService.deleteUser(id);
    }

    @ApiOperation("修改一条记录")
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public void updateUser(@RequestBody User user) {
        userService.updateUser(user);
    }

    @ApiOperation("查询一条记录")
    @RequestMapping(value = "/find/{id}", method = RequestMethod.GET)
    public User findUserById(@PathVariable Integer id) {
        return userService.findUserById(id);
    }
}

UserServiceImpl

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    public static final String CACHE_KEY = "user:";

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;
    
    @Override
    public void addUser(User user) {
        //第一步:先成功插入mysql
        int flag = userMapper.insert(user);
        if (flag > 0) {
            //第二步:需要再次查询一下mysql,把数据捞回来
            User queryUser = this.getById(user.getId());
            //第三步:将捞出来的user存进redis,完成新增功能的数据一致性
            redisTemplate.opsForValue().set(CACHE_KEY + queryUser .getId(), queryUser );
        } else {
            throw new RuntimeException("插入失败~");
        }
    }

    @Override
    public void deleteUser(Integer id) {
        //删除mysql中的数据
        boolean flag = this.removeById(id);
        if (flag) {
            //把redis中的数据删除
            redisTemplate.delete(CACHE_KEY + id);
        }
    }

    @Override
    public void updateUser(User user) {
        int flag = userMapper.updateById(user);
        if (flag > 0) {
            //将数据查出来放入redis
            user = userMapper.selectById(user.getId());
            redisTemplate.opsForValue().set(CACHE_KEY + user.getId(), user);
        }
    }

    /**
     * 业务逻辑并没有写错,对于中小厂(QPS <= 1000)可以使用,但是大厂不行,还有点问题
     *
     * @param id
     * @return
     */
    @Override
    public User findUserById(Integer id) {
        User queryUser = null;

        //1 现从redis里面查询,如果有直接返回结果,没有再去查询mysql
        queryUser = (User) redisTemplate.opsForValue().get(CACHE_KEY + id);
        if (queryUser == null) {
	        //redis里面无,继续查询mysql
            queryUser = userMapper.selectById(id);        
            if (queryUser == null) {
                //说明redis和mysql都没有数据
                return null;
            } else {
                //mysql中有,需要将数据写回redis,保证下一次的缓存命中率
                redisTemplate.opsForValue().set(CACHE_KEY + id, queryUser);
            }
        }
        return queryUser;
    }

    /**
     * 由于实际工作中,一般redis的key都是会设置过期时间的。如果突然碰到过期时间到期了,某个热点key突然失效,
     * 导致redis中的热点key突然失效导致缓存击穿的问题。
     * 
     * 加强:避免突然key失效了,打爆mysql,做一下预防,尽量不出现击穿的情况
     *
     * @param id
     * @return
     */
    public User getById2(Integer id) {
        User queryUser = null;
        //1 现从redis里面查询,如果有直接返回结果,没有再去查询mysql
        queryUser = (User) redisTemplate.opsForValue().get(CACHE_KEY + id);
        if (queryUser == null) {
            //2 对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysql(避免缓存击穿)
            synchronized (UserService.class) {
                queryUser = (User) redisTemplate.opsForValue().get(CACHE_KEY + id);
                //3 第二次查redis还是null,可以去查mysql了
                if (queryUser == null) {
                    queryUser = userMapper.selectById(id);
                    //mysql中也没有,直接返回null
                    if (queryUser == null) {
                        return null;
                    } else {
                        //5 mysql里面有数据,需要回写redis,完成数据一致性的同步工作
                        redisTemplate.opsForValue().setIfAbsent(CACHE_KEY + id, queryUser, 7, TimeUnit.DAYS);
                    }
                }
            }
        }
        return queryUser;
    }
}

3.2.6 测试

http://localhost:8001/doc.html

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
项目描述 在上家公司自己集成的一套系统,用了两个多月的时间完成的:Springboot+Mybatis-plus+ SpringMvc+Shiro+Redis企业级开发系统 Springboot作为容器,使用mybatis作为持久层框架 使用官方推荐的thymeleaf做为模板引擎,shiro作为安全框架,主流技术 几乎零XML,极简配置 两套UI实现(bootstrap+layer ui),可以自由切换 报表后端采用技术: SpringBoot整合SSM(Spring+Mybatis-plus+ SpringMvc),spring security 全注解式的权限管理和JWT方式禁用Session,采用redis存储token及权限信息 报表前端采用Bootstrap框架,结合Jquery Ajax,整合前端Layer.js(提供弹窗)+Bootstrap-table(数据列表展示)+ Bootstrap-Export(各种报表导出SQL,Excel,pdf等)框架,整合Echars,各类图表的展示(折线图,饼图,直方图等),使用了layui的弹出层、菜单、文件上传、富文本编辑、日历、选项卡、数据表格等 Oracle关系型数据库以及非关系型数据库(Redis),Oracle 性能调优(PL/SQL语言,SQL查询优化,存储过程等),用Redis做中间缓存,缓存数据 实现异步处理,定时任务,整合Quartz Job以及Spring Task 邮件管理功能, 整合spring-boot-starter-mail发送邮件等, 数据源:druid 用户管理,菜单管理,角色管理,代码生成 运行环境 jdk8+oracle+redis+IntelliJ IDEA+maven 项目技术(必填) Springboot+Mybatis-plus+ SpringMvc+Shiro+Redis 数据库文件 压缩包内 jar包文件 maven搭建 Springboot+Mybatis-plus+ SpringMvc+Shiro+Redis企业级报表后台管理系统 http://localhost:/8080/login admin admin Springboot+Mybatis-plus+ SpringMvc+Shiro+Redis企业级报表后台管理系统Springboot+Mybatis-plus+ SpringMvc+Shiro+Redis企业级报表后台管理系统Springboot+Mybatis-plus+ SpringMvc+Shiro+Redis企业级报表后台管理系统Springboot+Mybatis-plus+ SpringMvc+Shiro+Redis企业级报表后台管理系统Springboot+Mybatis-plus+ SpringMvc+Shiro+Redis企业级报表后台管理系统

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值