记录一个前段时间使用雪花算法时候遇到的坑。
场景大概是这样的:
我有一个设备信息实体类,设备id是 long 类型,映射到数据库中是一个设备表,主键id是 bigint 类型。id的生成策略是使用雪花算法。
使用postman调用接口,获取到的JSON是正常的
"id"
:
1297873308628307970
,
但是到了前端那边id的值却变成了1297873308628300000
仔细一比较发现这两个数值还挺像的,只有后4位不一样,当时立马想到了是不是精度丢失。
那么在哪个环节丢失了精度?用postman获取JSON的时候,数值是正常的,那么肯定不是后端丢失了精度,只可能是前端那边丢失了精度。
上网查了下,
JSON字符串转JS对象,JSON中的数字会转为 number 类型, number 类型的精度是16位,但是雪花算法生成的id长度有19位,so后面的几位精度就丢失了
。如果想要前端不丢失精度,JSON中的id就不能是long类型,改为String类型就好了。
问题找到了,但要怎么解决呢。一种方案是修改实体类中id的类型,将id改为String类型,并且将表的主键也改为varchar类型。但是字符串查询性能比数字差,所以这种方案不太推荐。
好在Jackson已经为我了提供了
三种方案
:
一种是直接在实体类的id属性上面加上注解
@JsonSerialize(using = ToStringSerializer.class)
这样一来,在后端依然是 long 类型,当实体类序列化成JSON的时候,在JSON中这个属性就会变成string类型。
上面这种办法有个缺点就是,如果有很多的实体类的id都是 long 类型,那就得给每一个都加上注解,这样未免有些麻烦。下面这个方法通过添加一个全局配置来使long类型转为JSON中的string类型,省去了一个一个添加注解的麻烦。
@Configuration
public class JacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
// 全局配置序列化返回 JSON 处理
SimpleModule simpleModule = new SimpleModule();
//JSON Long ==> String
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
return objectMapper;
第三种办法是在application.yml中加上以下配置,这个办法会将所有数字都变成字符串,包括long
和int
类型
spring:
jackson:
generator:
writeNumbersAsStrings: true
记录一个前段时间使用雪花算法时候遇到的坑。场景大概是这样的:我有一个设备信息实体类,设备id是 long 类型,映射到数据库中是一个设备表,主键id是 bigint 类型。id的生成策略是使用雪花算法。使用postman调用接口,获取到的JSON是正常的{ "id": 1297873308628307970, ... //其他属性省略}但是到了前端那边id的值却变成了1297873308628300000仔细一比较发现这两个数值还挺像的,只有后4位不一样,当时立马想到了是不是精度
问题描述:
后端把Long类型的数据传给前端,前端可能会出现精度丢失的情况。例如:201511200001725439这样一个Long类型的整数,传给前端后会变成201511200001725440
解决方法:
把Long类型再回传时强转为String
通过@JsonSerialize注解自动转换
这里主要描述第二种方法如何实现
只需要转换的字段加上注解,并指定using为 ToStringSerializer.class
@JsonSerialize(using = ToStringSerialize
1. 什么是Redis?它主要用来什么的?
Redis,英文全称是 Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
与MySQL数据库不同的是,Redis的数据是存在内存中的。它的读写速度非常快,每秒可以处理超过10万次读写操作。因此redis被 广泛应用于缓存,另外,Redis也经常用来做分布式锁。除此之外,Redis支持事务、持久化、LUA 脚本、L
场景:从数据库中查找出一条记录,返回为A对象,A对象的id变量为Long类型,该id值为1373995515313754114。现在控制器接收一个Long类型的参数demandId,值同样为1373995515313754114,此时在Service层有个条件判断恰好是要比较A.id与demandId是否相当。这个时候条件反应使用了if(A.id == demandId);该条件判断即使是两个值是相等的也会返回false。
原因:Long类型是包装类型,==比较符号比较的是两个对象的地址值,从而导致始终返回
在 Redis 中将
long 类型转换为 string 存入 hash 时,可能
会出现
精度丢失的情况。解决方法是在存入和取出时使用数字字符串,而不是直接使用
long 类型。
例如,在存入时将
long 转换为字符串:
long num = 1234567890;
String numString =
Long.toString(num);
redisTemplate.opsForHash().put("hash_name", "field_name", numString);
在取出时将字符串转换为
long:
Object value = redisTemplate.opsForHash().get("hash_name", "field_name");
long num =
Long.parse
Long((String)value);
总之,从Redis里存取
long值时,转成字符串存取,获取时再转成
long类型即可避免
精度丢失。
he_sk:
理解Spring定时任务@Scheduled的两个属性fixedRate和fixedDelay
抑郁的孤狼: