>  기사  >  Java  >  SpringBoot 눈송이 알고리즘의 기본 키 ID가 프런트 엔드로 전송된 후 정밀도 손실 문제를 해결하는 방법

SpringBoot 눈송이 알고리즘의 기본 키 ID가 프런트 엔드로 전송된 후 정밀도 손실 문제를 해결하는 방법

PHPz
PHPz앞으로
2023-05-11 12:34:182795검색

문제 설명

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 너비가 Front End의 너비보다 더 크다는 것을 알 수 있습니다. 눈송이 알고리즘은 일반적으로 18비트나 19비트 너비의 숫자를 생성하므로 이때 문제가 발생합니다.

프로젝트 시나리오

1. 테이블 구조

기본 키 유형은 눈송이 알고리즘에 의해 생성된 ID를 저장하는 BIGINT입니다.

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의 눈송이 알고리즘은 기본 키 ID로 19자리 순수 숫자를 자동으로 생성하는 데 사용됩니다. (물론 눈송이 알고리즘을 사용하여 수동으로 ID를 생성할 수도 있습니다)

import lombok.Data;
 
@Data
public class User {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
	
    //其他成员
}

3. 프런트엔드에 응답

JSON 데이터로 프런트엔드에 정상적으로 응답

{
  "id": 1352166380631257089,
   ...
}

문제 설명

Instances

Controller

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

Entity

package com.knife.entity;
 
import lombok.Data;
 
@Data
public class UserVO {
    private Long id;
 
    private String username;
}

Test

방문: http://localhost:8080/user/find?id=1352213368413982722

Result

SpringBoot 눈송이 알고리즘의 기본 키 ID가 프런트 엔드로 전송된 후 정밀도 손실 문제를 해결하는 방법

위에서 볼 수 있듯이 문제가 재현됩니다. , 문제 없습니다.

왜 문제가 없나요?

프런트엔드가 백엔드로 전달됨: SpingMVC는 자동으로 String 유형 ID를 Long 유형으로 변환하므로 문제가 없습니다. 백엔드는 프런트엔드에 응답합니다. JSON 형식이며 아무것도 없습니다.

언제 무엇이 잘못될 수 있나요?

프런트 엔드는 JSON을 수신한 후 이를 JS 개체로 직렬화한 다음 다른 작업을 수행합니다. JSON을 JS 개체로 변환할 때 다음과 같은 문제가 발생합니다.

SpringBoot 눈송이 알고리즘의 기본 키 ID가 프런트 엔드로 전송된 후 정밀도 손실 문제를 해결하는 방법 원래 ID는 1352213368413982722이고, JS 개체로 직렬화되면 1352213368413982700

코드는

const json = '{"id": 1352213368413982722, "name": "Tony"}';
const obj = JSON.parse(json);
 
console.log(obj.id);
console.log(obj.name);
가 됩니다.

솔루션

다음 두 가지 솔루션이 있습니다

    데이터베이스 테이블 디자인의 id 필드를 Long 유형에서 String 유형으로 변경합니다.
  • 프런트엔드는 ID를 저장하기 위해 String 타입을 사용하여 정확성을 유지하고, 백엔드와 데이터베이스는 계속 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

SpringBoot 눈송이 알고리즘의 기본 키 ID가 프런트 엔드로 전송된 후 정밀도 손실 문제를 해결하는 방법옵션 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

SpringBoot 눈송이 알고리즘의 기본 키 ID가 프런트 엔드로 전송된 후 정밀도 손실 문제를 해결하는 방법방법 2: 로컬 처리

지침

추가: @JsonSerialize( = ToStringSerializer.class)를 필드에 사용합니다.

Instance

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제