java 怎么保证余额_Java中如何存储金额的问题

背景分析:

在实际开发过程中,对于金额(一般是元为单位)前端输入一般为小数点两位,比如: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;

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值