Java バックエンド Long 型の範囲
の 19 桁目です。
##-2^53~2^53、つまり: -9007199254740991 ~9007199254740991、これは
この数値は、Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER メソッドを通じて取得できます。
Java バックエンドの Long width がフロントエンドの Long width よりも大きいことがわかります。スノーフレーク アルゴリズムは通常、18 ビットまたは 19 ビット幅の数値を生成するため、この時点で問題が発生します。
プロジェクト シナリオ主キーのタイプは 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; }
問題の再発
ご覧のとおり、以上であれば問題ありません。なぜ問題がないのでしょうか?
フロント エンドがバック エンドに渡される: 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);
次の 2 つの解決策があります。
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; } }
オプション 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 構成
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: ローカル処理
手順 @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; }
以上がSpringBootスノーフレークアルゴリズムの主キーIDがフロントエンドに送信された後の精度損失の問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。