最近在面试的时候,因为所写的简历上有一个金融方向的项目,面试官问了一句金融项目关于金额的数据存储使用什么数据类型.当时有些懵,因为我做的项目用的是hbase非关系型数据库.只有一种数据类型就是字符串.
回家后经过一番谷歌现在做一下笔记,以供大家参考.
在讲述前先来做一个小实验,提高一下大家的兴趣.
public class Test {
public static void main(String[] args) {
double a=0.03;
double b=0.02;
double c=a-b;
System.out.println(c);
}
}
# 输出结果为:
0.009999999999999998
这就是为什么面试官会问这个问题的原因所在.如果你回答的是float或者double,那恭喜你准备回家吧.
如果觉得不相信的同学可以动手在编辑器上实现并运行一下你会发现结果居然是0.009999999999999998,因为float与double都是浮点数,想让float或者double精确地表示0.1(或者10的任何负数次方值)是不可能的,浮点数参与的运算通常伴随着因为无法精确表示而进行的近似或舍入, 所以导致结果会有丝毫的偏差,而涉及金额的计算是绝对不予许存在偏差的。
这种舍入错误产生的原因是浮点数实际上是用二进制系统实现的,而分数1/10在二进制系统中没有精确的表示,其道理就如同在十进制系统中无法精确表示1/3一样;再比如0.5在二进制系统中有精确表示,而0.55在二进制系统中没有精确表示。
在java中使用BigDecimal来解决.
当使用double进行商业运算时,double计算会丢失精度时。可以使用BigDecimal进行计算。
import java.math.BigDecimal;
import org.junit.Test;
public class TestBigDecimal{
@Test
public void test(){
double a=0.1;
double b=0.2;
System.out.println(a+b);
BigDecimal a1=new BigDecimal("0.1");
BigDecimal b1=new BigDecimal("0.2");
System.out.println(a1+b1);
BigDecimal c1=new BigDecimal("0.23574");
a1=a1.add(c1);
a1=a1.setScale(1,BigDecimal.ROUND_DOWN);
System.out.println(a1);
}
}
//输出结果如下:
0.30000000000000004
0.3
0.4
double的加减无法精确计算出0.3,而使用BigDecimal却可以。
当然,如果直接将double传给BigDecimal,你会发现不但无法解决精度问题,反而对精度进行了补全。所以,为确保精度,我们将String传给它。
设置精度
- BigDecimal.setScale() // 方法用于格式化小数点
setScale(1) // 表示保留一位小数,默认用四舍五入方式
setScale(1,BigDecimal.ROUND_DOWN) // 直接删除多余的小数位,如2.35会变成2.3
setScale(1,BigDecimal.ROUND_UP) // 进位处理,2.35变成2.4
setScale(1,BigDecimal.ROUND_HALF_UP) // 四舍五入,2.35变成2.4
setScaler(1,BigDecimal.ROUND_HALF_DOWN) // 四舍五入,2.35变成2.3,如果是5则向下舍
.scale() 取精度值,即小数点后位数(注:BigDecimal可以通过setScale来提高精度,只要新设的值比原来的大!
BigDecimal也可以通过setScale来降低精度。因为新设的值比原来的小,所以必须保证原来数值的该位小数点后面都是0,只有这样才可以设比原来小的精度。
例:原来的值是:4.1235648,想把scale设为小于7为都会出错的,如果原来的值是:4.1235000,把scale设为小于4位会出错,而设为4、5、6、7都没有问题,设得更大,肯定不会出错)
加减乘除
- add(BigDecimal) BigDecimal对象中的值相加,然后返回这个对象。
- subtract(BigDecimal) BigDecimal对象中的值相减,然后返回这个对象。
- multiply(BigDecimal) BigDecimal对象中的值相乘,然后返回这个对象。
- divide(BigDecimal) BigDecimal对象中的值相除,然后返回这个对象。
除法详解
BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
1. 除数
2. 保留精度
3. 舍入规则
BigDecimal.ROUND_DOWN // 直接删除多余的小数位,如2.35会变成2.3
BigDecimal.ROUND_UP // 进位处理,2.35变成2.4
BigDecimal.ROUND_HALF_UP // 四舍五入,2.35变成2.4
BigDecimal.ROUND_HALF_DOWN // 四舍五入,2.35变成2.3,如果是5则向下舍
BigDecimal mData = new BigDecimal("9.655").setScale(2, BigDecimal.ROUND_HALF_UP);
----结果:-----
mData=9.66
- toString() 将BigDecimal对象的数值转换成字符串。
- doubleValue() 将BigDecimal对象中的值以双精度数返回。
- floatValue() 将BigDecimal对象中的值以单精度数返回。
- longValue() 将BigDecimal对象中的值以长整数返回。
- intValue() 将BigDecimal对象中的值以整数返回。
金融系统MySQL数据库规范
- 项目中所涉及到的表结构都需要增加创建时间和修改时间
- 项目中所涉及到的用户表中的id,都使用user_id
- 项目中所涉及到的记录的状态信息,必须使用枚举类型(对应java枚举类)
- 项目中所涉及到货币单位表示都使用decimal类型,并且保留8位小数
- 项目中所涉及到的账户流水表,请使用_record作为后缀,如果是普通操作日志或者系统记录日志请使用_log后缀
关键字段定义如下:
字段名 | 类型 | 备注 |
---|---|---|
id | bigint(20) | 自增长 |
create_time | datetime | 创建时间 |
update_time | datetime | 修改时间 |
user_id | bigint(20) | 用户id |
amount | decimal(20,8) | 货币类型金额 |
method | varchar(64) | 支付方式 |
status | varchar(64) | 状态信息 说明:PUBLIC 正常 DELETED 删除 LOCKED 锁定 |
时间处理相关
create_time
datetime NOT NULL COMMENT ‘创建时间’,
update_time
datetime NOT NULL COMMENT ‘本记录最后修改时间’,
在mybatis的*Mapper.xml文件中统一使用now()函数,如下:
<sql id="Base_Column_List">
id, openid, mobile, user_id, create_time, update_time
</sql>
<insert id="insert" parameterType="UserWechatPo">
INSERT INTO user_wechat
(openid, mobile,user_id, create_time, update_time)
VALUES
(#{openid}, #{mobile}, #{userId}, now(), now())
</insert>
<update id="update" parameterType="WechatMessage">
UPDATE
wechat_message
<set>
<if test="status != null">
status = #{status},
</if>
update_time = now()
</set>
WHERE id = #{id}
</update>
Decimal详解
Decimal为专门为财务相关问题设计的数据类型。DECIMAL从MySQL 5.1引入,列的声明语法是
DECIMAL(M,D)
在MySQL 5.1中,参量的取值范围如下:
- M是数字的最大数(精度)。其范围为1~65(在较旧的MySQL版本中,允许的范围是1~254),M 的默认 值是10。
- D是小数点右侧数字的数目(标度)。其范围是0~30,但不得超过M。
说明:float占4个字节,double占8个字节,decimail(M,D)
占M+2个字节。
如DECIMAL(5,2) 的最大值为9999.99
,因为有7 个字节可用。
能够解决数据的范围和精度的问题。
评论前必须登录!
注册