问题描述
测试阶段,由于存在某一功能的同时操作,该功能还是入库逻辑,此时若不进行处理,会造成插入表中多条重复数据,为此该问题需要修复。
解决办法
在接口开始进行对是否存在某个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分钟,该用户继续操作该数据,会提示“该用户已存在!”