Skip to main content

Search CooLWJ

选择搜索范围,输入关键词后按回车。

Language

Can Being Too Long Be Wrong?—An Unusual Journey to Fix Precision Loss in Backend Long IDs

Introduction

In this era of separated frontends and backends, our lives are full of countless opportunities and challenges—including those maddening bugs that appear out of nowhere. Today, we are going to discuss a classic issue that has left countless developers unsure whether to laugh or cry:Backend Long An excessively long type ID causes loss of precision on the frontend. Speaking of this issue, it really is the “root of all evil”—after all, JavaScript Can only safely handle Number.MAX_SAFE_INTEGER What about values within this range (that is, 9007199254740991)?

If you have ever racked your brains over this problem, don't worry—you are not alone. Today, we will not only dissect this “problem of the century” with humor, but also help you solve it at its root, so your code no longer “loses its precision.”

Background to the problem: Why Long That long?

First, we have to start with Long type as the starting point.Long, that is Java the 64-bit integer type in it, for those who like working with Big Data, and large numbers, this type is a godsend for Java. The frontend world, however, is a little “uninformed”: it only knows how to handle integers within 53 bits. Yes, you heard that right. In this respect, JavaScript Like an old pedant who is “a beat behind,” it starts fiddling with decimal points and exponents when faced with larger numbers, ultimately spitting out a number that leaves you in despair. And so, precision loss begins haunting your project like a ghost.

The First Encounter: Tales of Precision Loss

The story has to begin with a bug report one day: “Hey, developer, look—why has this ID changed? This isn’t what I Database that ID in it!” You look with a frown, and yes,9223372036854775807 became 9223372036854776000, goodness, those extra digits seem like magic. The backend clearly sent the correct value! Scratching your head, you think, “This has to be the frontend’s fault!”

Is the Frontend or the Backend to Blame?—Tracing the Root Cause

Actually, blaming the frontend for this problem is not entirely unreasonable. Let’s take a look at JavaScript A “weakness” when handling numbers.JavaScript of Number The type uses the double-precision floating-point format based on the IEEE 754 standard and can safely represent only 53 binary digits, that is, Number.MAX_SAFE_INTEGER the value of—9007199254740991.

In other words, integers outside this range,JavaScript will begin to “lose precision,” as its “mental capacity” suddenly becomes insufficient. So, your Long The type IDs become a bunch of less “sexy” numerical values in its eyes. That is precisely why the frontend has to make a little “sacrifice” when handling these absurdly long IDs, leaving your once-faithful ID truncated into an “interesting” new number.

From Shock to Composure: Finding a Solution

Now that we have found the root of the problem, it is time for us developers to show what we can do. Next, I will walk you through several solutions and explain the advantages and disadvantages of each. After all, all roads lead to Rome.

1. Convert directly to a string: simple and blunt, yet efficient

Faced with this problem, the first thing that comes to mind is surely the simplest, most brute-force approach: directly take Long of the type Data conversion into a string and that would do it! Since JavaScript is a die-hard fan of floating-point numbers, so let's just throw the problem back at it and tell it: “You only need to treat this as a string; don't worry about how long it is!”

How can we achieve this? It is actually very simple; use Jackson provided by ToStringSerializer, we can easily take Long Convert it to a string.

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;

public class UserDto {
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    // 其他字段
}


This way, when the backend returns UserDto when,Long of the type id will be serialized as a string, and the frontend will also receive a string, neatly resolving the precision problem. It is practically like sticking a note on JavaScript's forehead: “This is a string—no need to worry about it!”

2. The frontend uses BigInt:Enabling precise arithmetic even with large numbers

Of course, a blunt, straightforward approach does not suit every situation. Imagine that the frontend needs to perform some mathematical operation on this ID: converting it directly to a string would not be ideal. So how should the frontend handle these “extra-long” IDs?

Fortunately,JavaScript It does have its merits. It introduced BigInt Afterward, JavaScript Finally, no longer the fool who could only fiddle with decimal points.BigInt is a new primitive data type designed specifically to handle integers of arbitrary precision. You can do this:

const id = BigInt("9223372036854775807");
console.log(id + 1n); // 输出:9223372036854775808n

This allows you to handle precisely on the frontend Long data of this type, avoiding loss of precision. Of course, here is a small reminder:BigInt It is not supported by all browsers, so you need to ensure that your application's environment is compatible.

3. Custom Serialization: Simplifying Complex Problems

Although the methods above can already solve most problems, we sometimes encounter situations that require finer control. In such cases, Jackson ’s custom serializer comes in handy.

We can write a custom serializer to flexibly control Long the serialization process for fields of that type. For example, during serialization, we can decide whether to Long Convert it to a string, or handle it differently for specific conditions:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

public class CustomLongSerializer extends JsonSerializer<Long> {

    @Override
    public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (value != null) {
            gen.writeString(value.toString());
        }
    }
}


Then apply this custom serializer wherever needed:

import com.fasterxml.jackson.databind.annotation.JsonSerialize;

public class UserDto {
    @JsonSerialize(using = CustomLongSerializer.class)
    private Long id;
    // 其他字段
}


This way, you can control the entire serialization process and ensure that every Long can all be handled the way you want. Of course, although this approach is flexible, it is slightly more complicated, requires a few extra lines of code, and may increase maintenance costs.

4. Global Handling: A Time- and Effort-Saving Approach

If you want to resolve this globally and avoid the trouble of configuring annotations on every field, consider global configuration ObjectMapper。By using Spring Boot configure the global settings in ObjectMapper, you can make all the Long All type fields are automatically converted to strings.

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(Long.class, ToStringSerializer.instance);
        module.addSerializer(Long.TYPE, ToStringSerializer.instance);
        mapper.registerModule(module);
        return mapper;
    }
}


This settles it once and for all: you no longer have to worry about forgetting to configure a serializer for a particular field, and the entire project can enjoy lossless precision. Of course, when using a global configuration, remember that it may affect some that you do not want converted Long fields, so careful consideration is needed.

A Real Case: The Twists and Turns of Fixing a Bug

To help everyone better understand the practical effects of these solutions, let me share a story from a real project. This project involved managing user IDs. Because of the large number of users, the backend used Long unique identifiers of this type. Just days after the project went live, users began reporting: “Why does my ID look different?”

Step One: Trace the Root Cause

We first checked the frontend code and found JavaScript of Number The type indeed cannot accurately represent this 19-digit number, resulting in a loss of precision. Next, we examined the backend code and found that although Long data of this type is correct on the backend, but when passed through REST API The number lost precision when returned to the frontend.

Step Two: Choose an appropriate solution

To resolve the problem quickly, we decided to first adopt @JsonSerialize(using = ToStringSerializer.class) this simple and effective approach. With it, we successfully prevented the frontend from receiving numbers with lost precision.

However, the problem was not fully resolved. Later requirements called for the frontend to perform certain operations on IDs, such as sorting user IDs. At that point, strings proved somewhat inadequate.

Step Three: Use BigInt Solving frontend computation problems

So, we decided to introduce into the frontend BigInt. By using BigInt, the frontend can both store these extremely long IDs precisely and perform the necessary mathematical operations. Tests show that this approach works well across major mainstream browsers. Its only drawback is limited support for some older browser versions.

Step Four: The Final Global Configuration

To prevent similar problems from recurring, we decided to ToStringSerializer configured to apply globally. This allows all Long Data of these types is automatically converted to strings during serialization, ensuring data accuracy on the frontend while reducing duplicate configuration in the code.

Conclusion: The Terminator of Precision Loss

This example shows that, although Long Loss of precision in data types may seem simple, but it can introduce many hidden problems in real projects. By comparing and trying various solutions, we ultimately found the best approach for our project.

I hope this humorous yet detailed blog post will help you when handling Long type precision-loss issues, with fewer detours. If you have had similar experiences, feel free to share your story in the comments; your experience may offer further insights to other developers.

Finally, remember that code is like life: an occasional “loss” is nothing to fear; the key is finding the right “serializer” to get it back on track. May your code never again “lose precision,” staying accurate and running smoothly all the way!


Comments

Sending

Choose your theme

Your theme applies across the site.