Hello everyone, I am Yihang!
Yesterday afternoon, a friend asked the following question in the group:
The database uses bigint to store the ID of the record, and the Java code uses the Long type to map the record's corresponding ID value; the front end calls the SpringBoot interface to obtain the data. When debugging the back end, the Long type ID can get the value normally, but after returning to the front end, after the ID Half of the section was "stolen", and it is normal when the query ID is 1, 2, and 3; the data is as follows:
Comparison of the two values:
后端的值:1508733541883731970
前端的值:1508733541883732000
It's obvious that there 's an accuracy issue , but when you don't understand the details, it's easy to get confused.
Why is this so?
reason
This is because the precision of numbers in Javascript is limited, and the precision of Long in Java is beyond the processing range of Javascript. JS follows the IEEE 754 specification, uses double precision storage, and occupies 64 bits. Its structure is as shown in the figure:
- 1 bit (s) used to represent the sign bit
- 11 bits (e) are used to represent the exponent
- 52 bits (f) represents the mantissa
The maximum number of mantissa digits is 52, so the largest integer that can be accurately represented in JS is Math.pow(2, 53). In decimal, 9007199254740992
any number greater than 9007199254740992 will cause loss of precision;
In order to verify, we pressed in the browser F12
and did the following test in the Console:
the result is the same as what we thought above.
solution
However, in the actual development process, bigint in the database and Long in Java are relatively commonly used data types. It is impossible not to use them because of the accuracy problem of front-end JS. Therefore, in order to avoid loss of accuracy, for this relatively large numerical type , it can be returned in the form of text;
SpringBoot's object serialization is adopted by default Jackson
. There are the following three ways to convert numeric types into text strings.
Interfaces and objects used in testing:
@GetMapping("/user")
public User getUser(){
User user = new User();
user.setId(1508733541883731970L);
user.setAge(10);
user.setName("zhangsan");
user.setGender((short) 1);
return user;
}
@Data
class User{
Long id;
String name;
Integer age;
Short gender;
}
Method 1: Attribute serialization annotation @JsonSerialize
Specified properties in the object can be serialized as text
@Data
class User{
@JsonSerialize(using = ToStringSerializer.class)
Long id;
String name;
@JsonSerialize(using = ToStringSerializer.class)
Integer age;
Short gender;
}
Test Data:
{
"id": "1508733541883731970",
"name": "zhangsan",
"age": "10",
"gender": 1
}
@JsonSerialize
The configured id
sum age
is converted to text
-
advantage
Flexible, according to the attribute configuration of the object, you can change the one you want without disturbing other attributes or objects.
-
shortcoming
Each attribute that needs to be converted needs to be configured, which is a bit of hard work.
Method 2: Global configuration, convert numerical type to text
If you need to convert all number types into text, you can add the following configuration to application.yml:
spring:
jackson:
generator:
write_numbers_as_strings: true #序列化的时候,将数值类型全部转换成字符串返回
Test example:
{
"id": "1508733541883731970",
"name": "zhangsan",
"age": "10",
"gender": "1"
}
-
advantage:
After configuration, all numerical types are converted to text, once and for all;
-
shortcoming
The above advantages are also part of the disadvantages: they are too general and not flexible enough;
Method three, single type conversion
You can customize a Jackson object conversion constructor to convert the specified type in the specified serialization method. For example, it will be converted to text when it Long
encountersDouble
@Bean("jackson2ObjectMapperBuilderCustomizer")
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
Jackson2ObjectMapperBuilderCustomizer customizer = new Jackson2ObjectMapperBuilderCustomizer() {
@Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance)
.serializerByType(Long.TYPE, ToStringSerializer.instance);
}
};
return customizer;
}
Test Data:
{
"id": "1508733541883731970",
"name": "zhangsan",
"age": 10,
"gender": 1
}
After discovery, the id of Long type was converted into text; Integer
and Short
the id type was not affected;
The three methods have their own applicable scenarios. In comparison, the first and third methods are relatively common and can be chosen according to your actual situation;