springboot中如何同时操作同一功能

问题描述

测试阶段,由于存在某一功能的同时操作,该功能还是入库逻辑,此时若不进行处理,会造成插入表中多条重复数据,为此该问题需要修复。

解决办法

在接口开始进行对是否存在某个key值的判断,若不存在,则插入一条到redis中并加锁;若存在,则提示“正在处理中”;若中间出现逻辑处理异常,则需要对该key值删除;最后进行对锁的释放;

话不多说,上代码

pom.xml 依赖补充
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
application.yml文件中redis配置
redis:
  host: 127.0.0.1
  port: 6379
  timeout: 10
  poolMaxTotal: 1000
  poolMaxIdle: 500
  poolMaxWait: 500
UserMapper.java
import com.example.demo.entity.User;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {

    int add(User user);

    User queryByName(String name);
}
 UserMapper.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <select id="queryByName" resultMap="userResult">
        select  id,name,age  from  "USER"
        where name=#{name}
    </select>

    <insert id="add" parameterType="com.example.demo.entity.User">
      INSERT INTO "USER" (id,name, age)
      VALUES (SYS_GUID(),#{name},#{age})
    </insert>
</mapper>
RedisService类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;


@Service
public class RedisService {

    @Autowired
    RedisTemplate<String,Object> redisTemplate;

    /**
     * 加锁
     * @param key
     * @param value
     * @param expireTime
     * @return
     */
    public boolean lock(String key, String value, Long expireTime) {
        Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value);
        if (expireTime != null && expireTime > 0) {
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
        }
        return success != null && success;
    }

    /**
     * 释放锁
     * @param key
     */
    public void unlock(String key) {
        redisTemplate.opsForValue().getOperations().delete(key);
    }

    /**
     * 根据key删除信息
     * @param key
     */
    public void deleteStr(String key) {
        redisTemplate.delete(key);
    }
}
@bean配置 解决redis内容乱码,为了方便,我这边直接在启动类中配置

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用jackson的序列化方式
        template.setHashKeySerializer(jackson2JsonRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
controller类

该程序对新增用户功能同时操作的模拟,补充redis中key的判断,具体开发逻辑或内容可以视情况而定!

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.service.RedisService;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisService redisService;

    @PostMapping("/addTest")
    public void addTest(@RequestBody User user) throws Exception {

        //todo 该功能的状态校验
        //1.判断该用户在redis是否存在
        if (!redisService.lock("addUser", String.valueOf(System.currentTimeMillis()), 15L)) {
            throw new Exception("正在操作中");
        }
        try {
            //2.逻辑处理
            User user1 = userMapper.queryByName(user.getName());
            if (user1 != null) {
                throw new Exception("该用户" + user.getName() + "已存在!");
            }
            for (int i = 0; i < 1000; i++) {
                for (int j = 0; j < 1000; j++) {
                    logger.info("i*j={}", i * j);
                }
            }
            userMapper.add(user);
        } catch (Exception e) {
            redisService.deleteStr("addUser");
            throw e;
        } finally {
            redisService.unlock("addUser");
        }
    }
}

测试结果

一用户信息操作结果:

另一用户操作结果:

等待2分钟,该用户继续操作该数据,会提示“该用户已存在!”

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值