ホームページ > Java > &#&チュートリアル > SpringBootスノーフレークアルゴリズムの主キーIDがフロントエンドに送信された後の精度損失の問題を解決する方法

SpringBootスノーフレークアルゴリズムの主キーIDがフロントエンドに送信された後の精度損失の問題を解決する方法

PHPz
リリース: 2023-05-11 12:34:18
転載
2803 人が閲覧しました

問題の説明

Java バックエンド Long 型の範囲

  • ##-2^63~2^63、つまり -9223372036854775808~9223372036854775807 、これは

    の 19 桁目です。

  • この数値は、Long.MAX_VALUE、Long_MIN_VALUE メソッドを通じて取得できます。
フロントエンド JS の数値型の範囲

##-2^53~2^53、つまり: -9007199254740991 ~9007199254740991、これは
    16 ビット
  • です。

    この数値は、Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER メソッドを通じて取得できます。
  • 結論

Java バックエンドの Long width がフロントエンドの Long width よりも大きいことがわかります。スノーフレーク アルゴリズムは通常、18 ビットまたは 19 ビット幅の数値を生成するため、この時点で問題が発生します。

プロジェクト シナリオ

1. テーブル構造

主キーのタイプは BIGINT で、スノーフレーク アルゴリズムによって生成された ID が格納されます。

CREATE TABLE `user` (
  `id` BIGINT(32) NOT NULL COMMENT '用户id',
	...
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
ログイン後にコピー

2.Entity

データベース ID の BIGINT 型に対応するには Long 型を使用します。

ここでは、MybatisPlus のスノーフレーク アルゴリズムを使用して、19 桁の純粋な数値を主キー ID として自動的に生成します。 (もちろん、スノーフレーク アルゴリズムを使用して ID を手動で生成することもできます)

import lombok.Data;
 
@Data
public class User {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
	
    //其他成员
}
ログイン後にコピー

3. フロント エンドへの応答

JSON データでフロント エンドへの応答正常です

{
  "id": 1352166380631257089,
   ...
}
ログイン後にコピー

問題の説明

インスタンス

コントローラ

package com.knife.controller;
 
import com.knife.entity.UserVO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping("user")
public class UserController {
 
    @GetMapping("find")
    public UserVO find(Long id) {
        UserVO userVO = new UserVO();
        userVO.setId(id);
        userVO.setUsername("Tony");
 
        return userVO;
    }
}
ログイン後にコピー

エンティティ

package com.knife.entity;
 
import lombok.Data;
 
@Data
public class UserVO {
    private Long id;
 
    private String username;
}
ログイン後にコピー

テスト

アクセス: http ://localhost:8080/user/find ?id=1352213368413982722

結果

問題の再発SpringBootスノーフレークアルゴリズムの主キーIDがフロントエンドに送信された後の精度損失の問題を解決する方法

ご覧のとおり、以上であれば問題ありません。

なぜ問題がないのでしょうか?

フロント エンドがバック エンドに渡される: SpingMVC は String 型 ID を Long 型に自動的に変換しますので、問題はありません。バック エンドはフロント エンドに応答します。 JSON 形式なので、JS とは関係がなく、問題ありません。

フロントエンドは JSON を受信した後、それを JS オブジェクトにシリアル化し、他の操作を実行します。 JSON を JS オブジェクトに変換するときに、次のような問題が発生します。

ご覧のとおり、元の ID は 1352213368413982722 で、JS オブジェクトにシリアル化された後、 1352213368413982700

になります。

コードは次のとおりです。

const json = '{"id": 1352213368413982722, "name": "Tony"}';
const obj = JSON.parse(json);
 
console.log(obj.id);
console.log(obj.name);
ログイン後にコピー
SpringBootスノーフレークアルゴリズムの主キーIDがフロントエンドに送信された後の精度損失の問題を解決する方法解決策

次の 2 つの解決策があります。

ID フィールドを変更します。データベースのテーブル設計をLong型からString型へ。

#フロントエンドは精度を維持するために String 型を使用して ID を保存し、バックエンドとデータベースは引き続き Long (BigINT) 型を使用します
  • オプション 1 では String 型を使用します。データベース ID を実行すると、クエリのパフォーマンスが大幅に低下します。したがって、オプション 2 を採用する必要があります。この記事ではオプション 2 を紹介します。
  • 方法 1: グローバル処理

  • はじめに

ObjectMapper をカスタマイズします。

オプション 1: ToStringSerializer (推奨)

package com.knife.config;
 
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
 
@Configuration
public class JacksonConfig {
 
    @Bean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
 
        // 全局配置序列化返回 JSON 处理
        SimpleModule simpleModule = new SimpleModule();
        // 将使用String来序列化Long类型
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }
}
ログイン後にコピー
テスト

アクセス: http://localhost:8080/user/find?id=1352213368413982722

オプション 2: カスタム シリアライザー (非推奨)

シリアライザー

package com.knife.config;
 
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
 
import java.io.IOException;
 
/**
 * 超出 JS 最大最小值 处理
 */
@JacksonStdImpl
public class BigNumberSerializer extends NumberSerializer {
 
	/**
	 * 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来
	 */
	private static final long MAX_SAFE_INTEGER = 9007199254740991L;
	private static final long MIN_SAFE_INTEGER = -9007199254740991L;
 
	/**
	 * 提供实例
	 */
	public static final BigNumberSerializer instance = new BigNumberSerializer(Number.class);
 
	public BigNumberSerializer(Class<? extends Number> rawType) {
		super(rawType);
	}
 
	@Override
	public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
		// 超出范围 序列化位字符串
		if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
			super.serialize(value, gen, provider);
		} else {
			gen.writeString(value.toString());
		}
	}
}
ログイン後にコピー

ObjectMapper 構成SpringBootスノーフレークアルゴリズムの主キーIDがフロントエンドに送信された後の精度損失の問題を解決する方法

package com.knife.config;
 
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
 
@Configuration
public class JacksonConfig {
 
    @Bean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
 
        // 全局配置序列化返回 JSON 处理
        SimpleModule simpleModule = new SimpleModule();
        // 将使用自定义序列化器来序列化Long类型
        simpleModule.addSerializer(Long.class, BigNumberSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, BigNumberSerializer.instance);
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }
}
ログイン後にコピー

テスト

次を参照してください。 http://localhost:8080/user/find?id=1352213368413982722

方法 2: ローカル処理

手順

SpringBootスノーフレークアルゴリズムの主キーIDがフロントエンドに送信された後の精度損失の問題を解決する方法

@JsonSerialize(using= ToStringSerializer.class) をフィールドに追加します。

package com.knife.entity;
 
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
 
@Data
public class UserVO {
    @JsonSerialize(using= ToStringSerializer.class)
    private Long id;
 
    private String username;
}
ログイン後にコピー

テスト

アクセス: http://localhost:8080/user/find?id=1352213368413982722

以上がSpringBootスノーフレークアルゴリズムの主キーIDがフロントエンドに送信された後の精度損失の問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:yisu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート