记录一个前段时间使用雪花算法时候遇到的坑。
场景大概是这样的:
我有一个设备信息实体类,设备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