赞
踩
背景分析:
在实际开发过程中,对于金额(一般是元为单位)前端输入一般为小数点两位,比如:1.10,小数点第二位到分。而且数据库的存储粒度可以为分或者元,如果为分,则传入的值需要乘以100。
解决方法:
前端传入的的为小数点2位(小数点合法的位数是2位,大于2位前端和服务端都要校验),服务端这边用Decimal来接收金额类型数据,数据库存储的类型可以为bigInt(此时传入的Double或者Decimal要转为Long),或者为Decimal(此时传入的是Double);
注意:Spring MVC支持参数BigDecimal直接接收整数或者小数点的数
方法一:(传入的是BigDecimal,数据库保存为DECIMAL)
数据库字段定义如下:
`balance` DECIMAL(18, 2) NOT NULL DEFAULT "0.00" COMMENT "账户余额"方法二:(传入的是BigDecimal,数据库保存为BigInt)
数据库字段定义如下:
`total_amount` bigint(20) NOT NULL DEFAULT "0" COMMENT "总金额(单位: 分)",服务端校验BigDecimal的小数点为两位的方法:
代码示例如下:
/**
* 判断输入的值value的小数点数
* @param value
* @return
*/
public static int getDoublePrecision(BigDecimal bigDecimal) {
String valueStr = bigDecimal.toString();
int indexOf = valueStr.indexOf(".");
if (indexOf > 0) {
doublePrecision = valueStr.length() - 1 -indexOf;
}
return doublePrecision;
}
补充:BigDecimal的用法:
提供一个BigDecimal的加减乘除的用法(注意:不要用BigDecimal直接接收Double类型的值(使用BigDecimal中的String来接收)):
// 注意:不要用BigDecimal直接接收Double类型的值(使用BigDecimal中的String来接收)
BigDecimal bigDecimal1 = new BigDecimal(a);
log.info("bigDecimal1 is:{}", bigDecimal1); // 输出:bigDecimal1 is:1.1100000000000000976996261670137755572795867919921875
// 即用BigDecimal中String来接收
BigDecimal bigDecimal2 = new BigDecimal(Double.toString(a)); // 推荐
BigDecimal bigDecimal3 = new BigDecimal(a.toString());
log.info("bigDecimal2 is:{}, bigDecimal3 is:{}", bigDecimal2, bigDecimal3);
/**
* 加减乘除 demo
*/
// 加法
BigDecimal add1 = new BigDecimal("1.22");
BigDecimal add2 = new BigDecimal("2.33");
BigDecimal bigDecimalAdd = add1.add(add2);
Double valueAdd = bigDecimalAdd.doubleValue();
log.info("BigDecimal add is:{}", valueAdd);
// 减法
BigDecimal sub1 = new BigDecimal("4.55");
BigDecimal sub2 = new BigDecimal("2.13");
BigDecimal bigDecimalSub = sub1.subtract(sub2);
Double valueSub = bigDecimalSub.doubleValue();
log.info("BigDecimal sub is:{}", bigDecimalSub);
// 乘法
BigDecimal mul1 = new BigDecimal("1.33");
BigDecimal mul2 = new BigDecimal("6.41");
BigDecimal bigDecimalMul = mul1.multiply(mul2);
Double valueMul = bigDecimalMul.doubleValue();
log.info("BigDecimal multiply is:{}", valueMul);
// 除法
int scale = 2; // 保留两位小数
BigDecimal div1 = new BigDecimal("3.34");
BigDecimal div2 = new BigDecimal("1.37");
BigDecimal bigDecimalDiv = div1.divide(div2, scale, BigDecimal.ROUND_HALF_UP); // 四舍五入
Double valueDiv = bigDecimalDiv.doubleValue();
log.info("BigDecimal divide is:{}", bigDecimalDiv.doubleValue());
补充一个基于Java注解BigDecimal精度判断:
用法示例:
@Data
public class ValidParameterVo {
@BigDecimalMaxPrecision(value = 2, message = "输入金额的最大精度不能超过小数点2位")
private BigDecimal money;
}
这里用@Validated验证模块,当输入的精度大于2位小数,会提示如下错误:
{
"data": {
"errorCode": 1,
"message": "输入金额的最大精度不能超过小数点2位"
},
"status": 1
}
具体注解实现为两个类:BigDecimalMaxPrecision 和 BigDecimalMaxPrecisionValidator.java,代码如下:
BigDecimalMaxPrecision
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = {BigDecimalMaxPrecisionValidator.class})
@Documented
public @interface BigDecimalMaxPrecision {
int value() default 2;
String message() default "{com.sankuai.meituan.donation.common.validate.BigDecimalPrecision.message}";
Class>[] groups() default { };
Class extends Payload>[] payload() default { };
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@interface List {
BigDecimalMaxPrecision[] value();
}
}
BigDecimalMaxPrecisionValidator.java如下:
import lombok.extern.slf4j.Slf4j;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.math.BigDecimal;
@Slf4j
public class BigDecimalMaxPrecisionValidator implements ConstraintValidator {
private int value;
private String message;
@Override
public void initialize(BigDecimalMaxPrecision constraintAnnotation) {
this.value = constraintAnnotation.value();
this.message = constraintAnnotation.message();
}
@Override
public boolean isValid(BigDecimal bigDecimal, ConstraintValidatorContext context) {
if (bigDecimal == null) {
return true;
}
String bigDecimalStr = bigDecimal.toString();
int indexOf = bigDecimalStr.indexOf(".");
int doublePrecision;
if (indexOf > 0) {
doublePrecision = bigDecimalStr.length() - 1 -indexOf;
if (doublePrecision > value) {
log.warn("input bigDecimal value is:{}, precision is:{}, set max precision is:{}", bigDecimal, doublePrecision, value);
return false;
}
}
return true;
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。