Zum Hauptinhalt springen

CooLWJ durchsuchen

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

Sprache

Ist zu lang zu sein auch ein Fehler? — Die wundersame Reparaturreise beim Präzisionsverlust von Long-IDs aus dem Backend

Einleitung

Im Zeitalter getrennter Frontends und Backends ist unser Leben voller unzähliger Chancen und Herausforderungen——einschließlich jener plötzlich auftauchenden Bugs, die einen in den Wahnsinn treiben. Heute sprechen wir über ein klassisches Problem, bei dem unzählige Entwickler nicht wissen, ob sie lachen oder weinen sollen:Backend Long Eine zu lange Typ-ID führt zu Präzisionsverlust im Frontend。Wenn wir auf dieses Problem zu sprechen kommen, ist das wirklich die „Wurzel allen Übels“ – schließlich JavaScript kann nur sicher verarbeiten Number.MAX_SAFE_INTEGER(also 9007199254740991)liegen?

Falls du dir darüber schon einmal den Kopf zerbrochen hast, keine Sorge: Du bist damit nicht allein. Heute nehmen wir dieses „Jahrhundertproblem“ mit Humor auseinander und zeigen dir außerdem, wie du es an der Wurzel löst, damit dein Code nicht länger seine „Präzision verliert“.

Hintergrund des Problems:Warum Long So lang?

Zunächst müssen wir bei Long mit den Typen beginnen.Long, nämlich Java enthaltener 64-Bit-Ganzzahltyp, für diejenigen, die gerne Big Data,und großen Zahlen umgeht, ist dieser Typ für Java geradezu ein Segen. Die Frontend-Welt ist hingegen etwas „wenig bewandert“: Sie kann nur Ganzzahlen mit höchstens 53 Bit verarbeiten. Ja, du hast richtig gehört, in dieser Hinsicht JavaScript Wie ein alter Gelehrter, der immer „einen Takt hinterherhinkt“, beginnt es bei größeren Zahlen, mit Dezimalpunkten und Exponenten herumzuhantieren, und spuckt schließlich eine Zahl aus, die einen verzweifeln lässt. So beginnt das Problem des Präzisionsverlusts wie ein Gespenst durch dein Projekt zu spuken.

Die erste Begegnung: Geschichten vom Präzisionsverlust

Die Geschichte beginnt mit einer Bug-Meldung eines Tages: „Lieber Entwickler, schau mal, warum sieht diese ID plötzlich anders aus? Das ist doch nicht mein Datenbank die ID darin!“ Du schaust mit gerunzelter Stirn nach: Ja,9223372036854775807 wurde zu 9223372036854776000,ach du meine Güte, diese zusätzlichen Ziffern wirken geradezu wie Zauberei. Dabei liefert das Backend doch die richtigen Werte! Du kratzt dir am Kopf und denkst: „Das liegt bestimmt am Frontend!“

Liegt die Schuld beim Frontend oder beim Backend?——Der Ursache auf den Grund gehen

Eigentlich ist es auch nicht völlig unbegründet, dem Frontend die Schuld an diesem Problem zuzuschieben. Schauen wir uns an JavaScript Die „Schwachstelle“ beim Umgang mit Zahlen.JavaScript von Number Der Typ verwendet ein Gleitkommaformat mit doppelter Genauigkeit auf Grundlage des Standards IEEE 754 und kann nur 53 Binärstellen sicher darstellen, also Number.MAX_SAFE_INTEGER den Wert — 9007199254740991.

Mit anderen Worten, ganze Zahlen außerhalb dieses Bereichs,JavaScript beginnt die „Genauigkeit nachzulassen“, und seine „Gehirnkapazität“ reicht plötzlich nicht mehr aus. Dadurch wird dein Long Dadurch werden IDs dieses Typs in seinen Augen zu einem Haufen weniger „sexy“ Zahlenwerte. Genau deshalb muss das Frontend bei der Verarbeitung dieser absurd langen IDs ein kleines „Opfer“ bringen. Das Ergebnis: Deine ursprünglich unveränderte ID wird abgeschnitten und verwandelt sich in eine „interessante“ neue Zahl.

Vom Schock zur Ruhe: Auf der Suche nach einer Lösung

Nachdem die Ursache des Problems gefunden ist, sind wir Entwickler an der Reihe, unser Können zu zeigen. Im Folgenden werde ich dir mehrere Lösungsansätze genauer vorstellen und ihre jeweiligen Vor- und Nachteile erläutern. Schließlich führen viele Wege nach Rom.

1. Direkt in eine Zeichenfolge umwandeln: schlicht und grob, aber effizient

Bei einem solchen Problem denken wir sicher zuerst an die einfachste und brachialste Methode: direkt Long vom Typ Datenumwandlung Einfach in eine Zeichenfolge umwandeln, dann ist es doch gut! Da JavaScript ein verblendeter Fan von Gleitkommazahlen ist, geben wir das Problem einfach zurück und sagen ihm:“Du musst das nur als Zeichenkette behandeln, mach dir keine Gedanken über ihre Länge!”

Wie lässt sich das erreichen? Ganz einfach, mit Jackson bereitgestellt von ToStringSerializer,können wir ganz einfach Long in eine Zeichenfolge umwandeln.

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;
    // 其他字段
}


Wenn das Backend dann UserDto zu dem Zeitpunkt, als Long vom Typ id wird als Zeichenkette serialisiert, und auch das Frontend empfängt eine Zeichenkette. Damit löst sich das Präzisionsproblem ganz von selbst. Es ist geradezu so, als würde man JavaScript einen Klebezettel auf die Stirn heften: „Das hier ist eine Zeichenkette, darum brauchst du dich nicht zu kümmern!“

2. Im Frontend verwenden BigInt:Auch mit großen Zahlen präzise rechnen

Natürlich eignet sich die einfache, brachiale Methode nicht für jede Situation. Stell dir vor, das Frontend müsste mit dieser ID eine mathematische Berechnung durchführen. Sie direkt in einen String umzuwandeln wäre dann keine gute Lösung. Wie sollte das Frontend also mit diesen „überlangen“ IDs umgehen?

Glücklicherweise,JavaScript ist auch nicht völlig nutzlos. Eingeführt wurde BigInt danach,JavaScript Endlich ist es nicht mehr dieser Trottel, der nur mit Dezimalstellen herumspielen kann.BigInt ist ein neuer primitiver Datentyp speziell für ganze Zahlen mit beliebiger Genauigkeit. Du kannst so vorgehen:

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

So kannst du im Frontend präzise mit Long Daten dieses Typs, um Präzisionsverluste zu vermeiden. Dazu allerdings ein kleiner Hinweis:BigInt wird nicht von allen Browsern unterstützt, daher musst du sicherstellen, dass deine Anwendungsumgebung damit kompatibel ist.

3. Benutzerdefinierte Serialisierung: Komplexe Probleme vereinfachen

Obwohl die oben genannten Methoden bereits die meisten Probleme lösen können, stoßen wir manchmal auf Szenarien, die eine feinere Steuerung erfordern. In solchen Fällen,Jackson kommt der benutzerdefinierte Serialisierer zum Einsatz.

Wir können einen eigenen Serialisierer schreiben, um je nach Bedarf flexibel zu steuern Long den Serialisierungsprozess des Typfelds. Beispielsweise können wir bei der Serialisierung entscheiden, ob Long In einen String umwandeln oder je nach konkreten Bedingungen unterschiedlich behandeln:

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());
        }
    }
}


Anschließend diesen benutzerdefinierten Serialisierer an den benötigten Stellen verwenden:

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

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


So können Sie den gesamten Serialisierungsprozess kontrollieren und sicherstellen, dass jedes Long können alle auf die von dir gewünschte Weise verarbeitet werden. Diese Methode ist zwar flexibel, aber etwas komplexer, erfordert einige zusätzliche Codezeilen und kann auch den Wartungsaufwand erhöhen.

4. Globale Verarbeitung:Eine zeit- und arbeitssparende Lösung

Wenn du dieses Problem global lösen und dir die Konfiguration von Annotationen an jedem einzelnen Feld ersparen möchtest, kannst du eine globale Konfiguration in Betracht ziehen ObjectMapper。Indem in Spring Boot die globale Konfiguration vornehmen für ObjectMapper,kannst du alle Long Typfelder werden alle automatisch in Zeichenketten umgewandelt.

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;
    }
}


Damit ist das Problem ein für alle Mal gelöst: Du musst dir keine Sorgen machen, bei einem Feld die Konfiguration des Serialisierers zu vergessen, und das gesamte Projekt profitiert davon, dass keine Präzision verloren geht. Bei einer globalen Konfiguration solltest du allerdings beachten, dass sie möglicherweise auch bestimmte von dir nicht zur Umwandlung vorgesehene Long Feld, weshalb dies sorgfältig abgewogen werden muss.

Praxisfall: Ein Weg voller Wendungen zur Behebung eines Bugs

Damit Sie die praktische Wirkung dieser Lösungen besser verstehen, möchte ich eine Geschichte aus einem echten Projekt erzählen. In diesem Projekt ging es um die Verwaltung von Benutzer-IDs. Wegen der großen Benutzerzahl setzte das Backend auf Long eindeutige Kennungen dieses Typs. Wenige Tage nach dem Produktivstart meldeten sich bereits Nutzer mit der Rückfrage:“Warum sieht meine ID plötzlich anders aus?”。

Erster Schritt: Die Ursache des Problems aufspüren

Wir überprüften zunächst den Frontend-Code und stellten fest, dass JavaScript von Number Der Datentyp kann diese 19-stellige Zahl tatsächlich nicht exakt darstellen, wodurch ein Präzisionsverlust entstand. Anschließend sahen wir uns den Backend-Code an und stellten fest, dass zwar Long Daten dieses Typs sind im Backend korrekt, doch bei der Übertragung über REST API Bei der Rückgabe an das Frontend ging die Genauigkeit der Zahl verloren.

Zweiter Schritt:Eine geeignete Lösung auswählen

Um das Problem schnell zu lösen, beschlossen wir, zunächst Folgendes einzusetzen: @JsonSerialize(using = ToStringSerializer.class) diese einfache und wirksame Methode. Damit konnten wir erfolgreich verhindern, dass das Frontend Zahlen mit Präzisionsverlust empfängt.

Das Problem war jedoch noch nicht vollständig gelöst. Bei späteren Anforderungen musste das Frontend bestimmte Operationen mit IDs ausführen, etwa Benutzer-IDs sortieren. Dabei stießen Zeichenfolgen an ihre Grenzen.

Schritt drei:Verwendung von BigInt Rechenprobleme im Frontend lösen

Also beschlossen wir, im Frontend Folgendes einzuführen BigInt。Durch die Verwendung von BigInt,kann das Frontend diese überlangen IDs sowohl exakt speichern als auch die notwendigen mathematischen Operationen durchführen. Tests haben gezeigt, dass diese Methode in allen gängigen Browsern gut funktioniert. Ihr einziger Nachteil ist die eingeschränkte Unterstützung einiger älterer Browserversionen.

Vierter Schritt:Die endgültige globale Konfiguration

Um zu verhindern, dass ähnliche Probleme künftig erneut auftreten, haben wir beschlossen, ToStringSerializer so konfigurieren, dass es global wirksam ist. Dadurch werden alle Long Die typisierten Daten werden bei der Serialisierung automatisch in Zeichenketten umgewandelt. Das gewährleistet die Datengenauigkeit im Frontend und reduziert zugleich wiederholte Konfigurationen im Code.

Schlusswort: Das Ende des Präzisionsverlusts

An diesem Beispiel lässt sich erkennen, dass zwar Long Das Problem des Genauigkeitsverlusts bei diesem Typ scheint einfach, kann in realen Projekten jedoch zahlreiche verborgene Risiken bergen. Durch den Vergleich und das Erproben verschiedener Lösungen fanden wir schließlich die für unser Projekt am besten geeignete Lösung.

Ich hoffe, dass dieser humorvolle und zugleich ausführliche Blogbeitrag dir beim Umgang mit Long bei Problemen mit dem Präzisionsverlust von Datentypen unnötige Umwege zu vermeiden. Wenn du Ähnliches erlebt hast, teile deine Geschichte gerne in den Kommentaren. Vielleicht liefern deine Erfahrungen anderen Entwicklern weitere Anregungen.

Denkt zum Schluss daran: Code ist wie das Leben. Ein gelegentlicher „Verlust“ ist kein Grund zur Angst; entscheidend ist, den passenden „Serialisierer“ zu finden, der alles wieder in die richtigen Bahnen lenkt. Möge euer Code nie wieder „Präzision verlieren“, stets genau bleiben und reibungslos laufen!


Kommentare

Wird gesendet

Design auswählen

Das Design gilt für die gesamte Website.