博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java中如何存储金额的问题
阅读量:4220 次
发布时间:2019-05-26

本文共 5177 字,大约阅读时间需要 17 分钟。

背景分析:
在实际开发过程中,对于金额(一般是元为单位)前端输入一般为小数点两位,比如: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精度判断:
用法示例:
@Datapublic 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})@Documentedpublic @interface BigDecimalMaxPrecision {        int value() default 2;        String message() default "{com.sankuai.meituan.donation.common.validate.BigDecimalPrecision.message}";        Class
[] groups() default { }; Class
[] 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;@Slf4jpublic 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; }}

转载地址:http://rgomi.baihongyu.com/

你可能感兴趣的文章
Oracle 11g 新特性 -- 自动诊断资料档案库(ADR) 说明
查看>>
Oracle 11g 新特性 -- RMAN Data Recovery Advisor(DRA) 说明
查看>>
CSDN博客之星 投票说明
查看>>
Oracle wallet 配置 说明
查看>>
Oracle smon_scn_time 表 说明
查看>>
VBox fdisk 不显示 添加的硬盘 解决方法
查看>>
Secure CRT 自动记录日志 配置 小记
查看>>
RMAN RAC 到 单实例 duplicate 自动分配通道 触发 ORA-19505 错误
查看>>
mysql 随机分页的优化
查看>>
DB2快速创建测试库
查看>>
利用db2look查看ddl
查看>>
SD卡驱动分析--基于高通平台
查看>>
[图文] Seata AT 模式分布式事务源码分析
查看>>
pm 源码分析
查看>>
Sending the User to Another App
查看>>
kmsg_dump
查看>>
Getting a Result from an Activity
查看>>
Allowing Other Apps to Start Your Activity
查看>>
dev/mem
查看>>
pfn_valid 源码分析
查看>>