一、Jackson對Long類型的處理導致精度丟失的問題
表的某一個字段的類型是 BIGINT,對應的 Java 類的屬性的類型就是 Long。當這個字段的值由后端返回給前端網(wǎng)頁時,發(fā)現(xiàn)了精度丟失的問題。比如后端返回的值是 588085469986509185,到了前端是 588085469986509200,后面的幾位數(shù)變成了 0,精度丟失了
二、原因
JavaScript 中數(shù)字的精度是有限的,BIGINT 類型的的數(shù)字超出了 JavaScript 的處理范圍。JavaScript 遵循 IEEE 754 規(guī)范,采用雙精度存儲(double precision),占用 64 bit,各位的含義如下:
1位(s) 用來表示符號位
11位(e) 用來表示指數(shù)
52位(f) 表示尾數(shù)
尾數(shù)位最大是 52 位,因此 JS 中能精準表示的最大整數(shù)是 Math.pow(2, 53),十進制即 9007199254740992。而 BIGINT 類型的有效位數(shù)是63位(扣除一位符號位),其最大值為:Math.pow(2, 63)。任何大于 9007199254740992 的就可能會丟失精度
三、解決方法
解決辦法就是讓 JavaScript 把數(shù)字當成字符串進行處理。對 JavaScript 來說,不進行運算,數(shù)字和?字符串處理起來沒有什么區(qū)別,在 Springboot 中處理方法基本上有以下幾種:
3.1 配置參數(shù) write_numbers_as_strings
Jackson 有個配置參數(shù) WRITE_NUMBERS_AS_STRINGS,可以強制將所有數(shù)字全部轉(zhuǎn)成字符串輸出。其功能介紹為:Feature that forces all Java numbers to be written as JSON strings.
使用方法很簡單,只需要配置參數(shù)即可?
spring:
jackson:
generator:
write_numbers_as_strings: true
這種方式的優(yōu)點是使用方便,不需要調(diào)整代碼;缺點是顆粒度太大,所有的數(shù)字都被轉(zhuǎn)成字符串輸出了,包括按照 timestamp 格式輸出的時間也是如此
3.2 給 Java 類的屬性單獨加注解
@JsonSerialize(using=ToStringSerializer.class)
private Long userId;
指定了 ToStringSerializer 進行序列化,將數(shù)字編碼成字符串格式。這種方式的優(yōu)點是顆粒度可以很精細;缺點同樣是太精細,如果需要調(diào)整的字段比較多會比較麻煩
3.3 自定義ObjectMapper
最后想到可以單獨根據(jù)類型進行設置,只對 Long 型數(shù)據(jù)進行處理,轉(zhuǎn)換成字符串,
而對其他類型的數(shù)字不做處理。Jackson 提供了這種支持。方法是對 ObjectMapper 進行定制。根據(jù) SpringBoot 的官方文檔,找到一種相對簡單的方法,只對 ObjectMapper 進行定制,而不是完全從頭定制,方法如下:
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return jacksonObjectMapperBuilder -> {
// long -> String
jacksonObjectMapperBuilder.serializerByType(Long.TYPE, ToStringSerializer.instance);
// Long -> String
jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance);
};
}
}
如果是 Json 工具類,可以這么設置文章來源:http://www.zghlxwxcb.cn/news/detail-832675.html
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
public class JsonUtil {
private static ObjectMapper mapper = new ObjectMapper();
static {
SimpleModule simpleModule = new SimpleModule();
// long -> String
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
// Long -> String
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
mapper.registerModule(simpleModule);
}
}
四、補充
如果方法3配置JacksonConfig不起作用但方法2起作用,請檢查是否配置了MVC,MVC級別比配置JacksonConfig要高,請在WebMvcConfig里配置Long轉(zhuǎn)換String文章來源地址http://www.zghlxwxcb.cn/news/detail-832675.html
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* 配置轉(zhuǎn)換器
* @return
*/
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(getObjectMapper());
return converter;
}
private ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// 指定時區(qū)
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
// 日期類型字符串處理
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// Java8日期處理
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
objectMapper.registerModule(javaTimeModule);
//將Long序列成String
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
return objectMapper;
}
}
到了這里,關于SpringBoot解決前端js處理大數(shù)字丟失精度問題Long轉(zhuǎn)String的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!