Lewati ke konten utama

Cari di CooLWJ

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

Bahasa

Terlalu panjang juga salah?——Perjalanan unik memperbaiki hilangnya presisi ID bertipe Long dari backend

Pendahuluan

Di era pemisahan frontend dan backend, hidup kita dipenuhi peluang dan tantangan yang tak terhitung jumlahnya—termasuk Bug yang tiba-tiba muncul dan membuat kita frustrasi. Hari ini kita akan membahas masalah klasik yang membuat banyak pengembang tak tahu harus tertawa atau menangis:Backend Long ID tipe yang terlalu panjang menyebabkan hilangnya presisi di frontend. Kalau membahas masalah ini, sungguh itulah “sumber segala kejahatan”, habisnya JavaScript Hanya dapat menangani dengan aman Number.MAX_SAFE_INTEGER nilai yang berada dalam batas(yaitu 9007199254740991)?

Jika Anda pernah memutar otak karena masalah ini, tenang saja, Anda tidak sendirian. Hari ini, kita bukan hanya akan mengupas “masalah abad ini” dengan humor, tetapi juga akan menuntun Anda menyelesaikannya hingga ke akar, agar kode Anda tidak lagi “kehilangan presisi”.

Latar belakang masalah:Mengapa Long Selama itu?

Pertama, kita perlu mulai dari Long jenis sebagai titik awal pembahasan.Long,yaitu Java tipe bilangan bulat 64 bit di dalamnya, bagi yang suka mengolah Data besar、angka besar di Java,tipe ini benar-benar merupakan anugerah。Namun,dunia frontend agak “kurang wawasan”,karena hanya mampu menangani bilangan bulat hingga 53 bit。Ya,kamu tidak salah dengar,dalam hal ini JavaScript Seperti seorang cendekiawan tua yang “terlambat setengah ketukan”, ketika berhadapan dengan angka yang lebih besar, ia mulai mengutak-atik titik desimal dan eksponen, lalu akhirnya mengeluarkan angka yang membuatmu putus asa. Maka, masalah hilangnya presisi pun mulai berkeliaran di proyekmu seperti hantu.

Pertemuan pertama:kisah-kisah hilangnya presisi

Kisah ini bermula dari laporan Bug pada suatu hari: “Kak pengembang, lihat, mengapa ID ini berubah bentuk? Ini bukan yang saya Basis data ID yang ada di dalamnya itu!” Kamu melihatnya sambil mengernyitkan dahi, benar, 9223372036854775807 berubah menjadi 9223372036854776000,aduh, angka tambahan ini benar-benar seperti sulap. Padahal data yang diberikan backend sudah benar! Sambil menggaruk kepala, Anda berpikir:“Ini pasti kesalahan frontend!”

Salah frontend atau backend?——Menelusuri akar masalah

Sebenarnya,menyalahkan pihak frontend atas masalah ini juga tidak sepenuhnya tanpa alasan。Mari kita lihat JavaScript “kelemahan” saat menangani angka.JavaScript milik Number Tipenya menggunakan format bilangan titik mengambang presisi ganda berdasarkan standar IEEE 754, yang hanya dapat merepresentasikan 53 digit biner secara aman, yaitu Number.MAX_SAFE_INTEGER nilainya——9007199254740991.

Dengan kata lain, bilangan bulat yang melampaui rentang ini,JavaScript akan mulai mengalami “penurunan akurasi”, dan “kapasitas otaknya” tiba-tiba tidak lagi mencukupi. Akibatnya, milik Anda Long ID dengan tipe tersebut pun berubah menjadi sekumpulan nilai yang tidak begitu “seksi” di matanya. Karena itulah, saat menangani ID yang panjangnya kelewat batas ini, antarmuka depan terpaksa sedikit “berkorban”. Hasilnya, ID Anda yang semula setia pada nilai aslinya terpotong dan berubah menjadi angka baru yang “menarik”.

Dari terkejut menjadi tenang:Mencari solusi

Karena akar masalahnya sudah ditemukan, kini giliran kita para pengembang untuk menunjukkan kemampuan. Selanjutnya, saya akan mengajak Anda memahami beberapa solusi secara mendalam dan menjelaskan kelebihan serta kekurangan masing-masing, karena semua jalan menuju Roma.

1. Langsung mengubahnya menjadi string:sederhana dan lugas, tetapi efisien

Menghadapi masalah seperti ini, hal pertama yang terpikir oleh kita tentu adalah cara paling sederhana dan langsung:langsung mengambil Long bertipe Konversi data ubah saja menjadi string, beres kan!Karena JavaScript adalah penggemar fanatik bilangan titik mengambang, jadi mari kita lemparkan saja masalahnya kembali kepadanya dan katakan:“Kamu hanya perlu menganggap ini sebagai string, jangan khawatir seberapa panjangnya!”

Bagaimana cara melakukannya? Sebenarnya sangat sederhana, gunakan Jackson yang disediakan ToStringSerializer,kita dapat dengan mudah mengubah Long diubah menjadi 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;
    // 其他字段
}


Dengan demikian, ketika backend mengembalikan UserDto ketika itu,Long bertipe id akan diserialisasi menjadi string, dan frontend juga menerima string, sehingga masalah presisi pun teratasi. Persis seperti menempelkan catatan kecil di kepala JavaScript:“Ini string, ya, kamu tidak perlu memikirkannya!”

2. Penggunaan di frontend BigInt:memungkinkan perhitungan bilangan besar tetap presisi

Tentu saja,cara yang sederhana dan langsung tidak selalu cocok untuk semua situasi。Bayangkan,jika frontend perlu melakukan operasi matematika tertentu pada ID ini,langsung mengubahnya menjadi string tentu kurang tepat。Lalu,bagaimana frontend seharusnya menangani ID yang “terlalu panjang” ini?

Untungnya,JavaScript juga bukan tanpa manfaat sama sekali. Memperkenalkan BigInt Setelah itu,JavaScript Akhirnya aku bukan lagi si bodoh yang hanya bisa mengutak-atik titik desimal.BigInt merupakan tipe data primitif baru yang khusus digunakan untuk menangani bilangan bulat dengan presisi arbitrer. Anda dapat melakukannya seperti ini:

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

Dengan demikian, Anda dapat menangani secara tepat di frontend Long data bertipe tersebut, untuk menghindari masalah hilangnya presisi. Tentu saja, ada sedikit pengingat di sini:BigInt Tidak semua peramban mendukungnya, jadi Anda perlu memastikan bahwa lingkungan aplikasi Anda kompatibel.

3. Serialisasi khusus:menyederhanakan masalah rumit

Meskipun metode di atas sudah dapat menyelesaikan sebagian besar masalah, terkadang kita menjumpai situasi yang memerlukan kendali lebih terperinci. Pada saat seperti ini,Jackson serializer kustomnya pun berguna.

Kita dapat menulis serializer khusus,untuk mengendalikan secara fleksibel sesuai kebutuhan Long proses serialisasi bidang bertipe tersebut. Misalnya, saat serialisasi kita dapat menentukan apakah akan Long diubah menjadi string, atau ditangani secara berbeda berdasarkan kondisi tertentu:

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


Kemudian terapkan serializer kustom ini di tempat yang memerlukannya:

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

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


Dengan demikian, Anda dapat mengendalikan seluruh proses serialisasi, memastikan setiap Long semuanya dapat diproses sesuai cara yang Anda inginkan. Tentu saja, meskipun fleksibel, cara ini sedikit lebih rumit, memerlukan beberapa baris kode tambahan, dan juga dapat meningkatkan biaya pemeliharaan.

4. Penanganan global:Solusi hemat waktu dan tenaga

Jika Anda ingin menyelesaikan masalah ini secara global agar tidak perlu mengonfigurasi anotasi pada setiap kolom, Anda dapat mempertimbangkan konfigurasi global ObjectMapper。Dengan melakukan pada Spring Boot mengonfigurasi pengaturan global di dalam ObjectMapper,Anda dapat membuat semua Long Semua bidang tipe secara otomatis dikonversi menjadi string.

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


Selesai sekali untuk seterusnya, tanpa perlu khawatir lupa mengonfigurasi serializer untuk suatu field, dan seluruh proyek dapat menikmati terjaganya presisi. Tentu saja, saat menggunakan konfigurasi global, perlu diperhatikan bahwa hal ini mungkin memengaruhi beberapa yang tidak ingin Anda konversi Long kolom, sehingga perlu dipertimbangkan dengan hati-hati.

Kasus nyata:perjalanan mengatasi bug yang penuh liku

Agar semua orang lebih memahami dampak nyata solusi-solusi ini, saya akan membagikan sebuah kisah dari proyek sungguhan. Proyek ini melibatkan pengelolaan ID pengguna. Karena jumlah penggunanya sangat besar, sistem backend menggunakan Long pengenal unik untuk tipe tersebut. Baru beberapa hari proyek diluncurkan, sudah muncul umpan balik pengguna:“Mengapa ID saya berubah bentuk?”.

Langkah pertama:menelusuri akar masalah

Pertama-tama kami memeriksa kode frontend dan menemukan bahwa JavaScript milik Number tipe ini memang tidak mampu merepresentasikan angka sepanjang 19 digit ini secara akurat, sehingga terjadi kehilangan presisi. Selanjutnya, kami memeriksa kode backend dan menemukan bahwa meskipun Long data bertipe tersebut sudah benar di sisi backend, tetapi saat melalui REST API Saat dikembalikan ke antarmuka, presisi angkanya hilang.

Langkah kedua: Memilih solusi yang sesuai

Untuk menyelesaikan masalah dengan cepat,kami memutuskan untuk terlebih dahulu menggunakan @JsonSerialize(using = ToStringSerializer.class) cara yang sederhana dan efektif ini. Melalui cara ini, kami berhasil mencegah bagian frontend menerima angka yang telah kehilangan presisi.

Namun, masalahnya belum sepenuhnya terselesaikan. Dalam kebutuhan berikutnya, antarmuka perlu melakukan operasi tertentu terhadap ID, misalnya mengurutkan ID pengguna. Pada saat itu, string mulai terasa kurang memadai.

Langkah ketiga: gunakan BigInt Mengatasi masalah komputasi di frontend

Maka, kami memutuskan untuk memperkenalkan di sisi frontend BigInt。Dengan menggunakan BigInt,frontend tidak hanya dapat menyimpan ID yang sangat panjang ini secara akurat, tetapi juga dapat melakukan operasi matematika yang diperlukan. Pengujian menunjukkan bahwa cara ini bekerja dengan baik pada berbagai peramban utama, dan satu-satunya kekurangannya adalah dukungan yang kurang baik untuk beberapa peramban versi lama.

Langkah keempat:Konfigurasi global akhir

Untuk mencegah masalah serupa terulang di masa mendatang, kami memutuskan untuk ToStringSerializer dikonfigurasi agar berlaku secara global. Ini membuat semua Long data bertipe tersebut secara otomatis diubah menjadi string saat serialisasi, sehingga keakuratan data di frontend terjamin sekaligus mengurangi konfigurasi kode yang berulang.

Penutup: Pengakhir Hilangnya Presisi

Dari kasus ini dapat dilihat,meskipun Long Masalah hilangnya presisi pada tipe data tampak sederhana, tetapi dapat menimbulkan berbagai risiko tersembunyi dalam proyek nyata. Dengan membandingkan dan mencoba berbagai solusi, akhirnya kami menemukan solusi terbaik yang sesuai untuk proyek kami.

Semoga tulisan blog yang jenaka sekaligus terperinci ini dapat membantu Anda saat menangani Long agar tidak terlalu banyak mengambil jalan memutar saat menghadapi masalah kehilangan presisi tipe. Jika Anda juga pernah mengalami hal serupa, silakan bagikan kisah Anda di kolom komentar; mungkin pengalaman Anda dapat memberikan lebih banyak inspirasi bagi pengembang lain.

Terakhir, ingatlah, kode itu seperti kehidupan: sesekali mengalami “kehilangan” bukanlah hal yang menakutkan, yang penting adalah menemukan “serializer” yang tepat untuk mengembalikannya ke jalur semula. Semoga kode Anda tidak lagi “kehilangan presisi”, tetap akurat, dan berjalan lancar sepanjang jalan!


Komentar

Mengirim

Pilih tema favorit

Tema pilihan berlaku di seluruh situs.