近期开发经验汇总
BigDecimal相关问题
BigDecimal比较大小
比较相等的时候,需要注意BigDecimal
的存储是由原数和小数位数(scale
)组成的,所以推荐使用 compareTo()
方法,如果使用equals()
比较相等,即要求比较双方数值和小数位数都一致,阿里规范禁止使用equals()
比较BigDecimal
。
BigDecimal 除不尽的问题
遇到异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal
原因是:计算结果是无限循环小数...
要设置舍入模式方法,设置为 BigDecimal.ROUND_HALF_DOWN
或 RoundingMode.DOWN
。同时要注意在进行一些金额、数量、百分比计算的时候,都建议涉及舍入模式,需要根据自己的业务规范制定,比如我们的业务规定数据库存储金额的小数位是4位,百分比是2位,计算金额和优惠率的结果都设置小数位为2位,并且全部向0舍去(RoundingMode.DOWN
)
new BigDecimal(16).divide(new BigDecimal(233), 2, RoundingMode.DOWN)
其余几种舍入模式参考 Bigdecimal 八种舍入模式
BigDecimal 的参数校验
使用 java validated 校验注解校验BigDecimal的请求参数,在保存优惠率、金额的时候会使用,完整使用如下:
/**
* 总金额
* totalMoney 类型是 BigDecimal, @Digits, @DecimalMin 是针对数值的校验
* 使用字符串传值,校验也能生效,只要字符串里面是数字就行
*/
@Digits(integer = 9, fraction=4, message = "金额格式不正确")
@DecimalMin(value = "0.00", message = "金额请勿小于0.00")
@DecimalMax(value = "100000000.00", message = "金额请勿超过1亿")
@NotNull(message = "请传递总金额")
private BigDecimal totalMoney;
该参数的请求对象前需加上
@Validated
或@Valid
注解,如果该对象嵌套在 请求对象的子对象里面,需要使用@Valid
和@Validated
来实现嵌套参数校验。
@Digits(integer = 9, fraction=4, message = "金额格式不正确")
digit是数位的意思,这里的integer
意思整数最多有几位,fraction
意思小数最多有几位,只是划定了传入参数的范围,前端传值的时候可以用字符串(只要字符串里面都是数字就行)、数字;
@DecimalMin(value = "0.00", message = "金额请勿小于0.00")
@DecimalMax(value = "100000000.00", message = "金额请勿超过1亿")
decimal的最小值 和最大值 ,传入参数必须在两个注解的value
范围内 ;
@NotNull(message = "amount不为空")
传入参数不为空,对于BigDecimal、Integer、Long等数值类型,用@NotNull
注解,如果传参是空字符串或者只有空格的字符串,也无法通过校验。如果字段的类型是String
,建议使用@NotBlank
注解
使用 easyExcel 导出时的问题
使用 cglib_使用easyexcel时遇到Could not initialize class cglib.beans.BeanMap,其实是底层的asm依赖冲突,可以通过 maven 插件查看冲突的 asm 依赖
解决办法:都排除掉,导入一个统一版本的依赖即可!
Latex 如何让等号对齐
\begin{equation*}
\begin{aligned}
f(n) &= f(n-1) + f(n-2) \\
&= f(n-2) + f(n-3) + f(n-2) \\
&= 2f(n-2) + f(n-3) \\
\end{aligned}
\end{equation*}
Stream 只能用一次,如何重用?
在开发中,有时会对一个列表做很多操作,如果使用Stream ,我们习惯性的将 Stream 抽离出来,然后对中间状态的流做不同的操作,这样会抛出异常,为什么呢?
这里回顾下流的特性:
- 流不会储存元素,所以每个Stream 流只能使用一次(每次调用方法后都会销毁)
- 流不会修改其数据源,只是在原数据集上定义了一组操作。不存储数据,也不修改原始源。
- 流执行具有延迟特性,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。
- 当执行流的终结操作的时候,整个流才会被执行
知道了流的特性,如果我们复用流,就会抛出异常,请看下面的例子
/**
* 计算 BigDecimal 的计算
*
* @date 2022/4/11 14:45:25
*/
public static void main(String[] args) {
ArrayList<Math> maths = new ArrayList<>();
maths.add(new Math(new BigDecimal("1"), new BigDecimal("13")));
maths.add(new Math(new BigDecimal("2"), BigDecimal.TEN));
// 计算 math 这个对象 里面的两个 BigDecimal 的 乘积
BigDecimal multiplyResult = maths.stream()
// 模拟 过滤操作
.filter(math -> true)
// 乘法
.map(accessory -> Optional.ofNullable(accessory.getPrice())
.orElse(BigDecimal.ZERO).multiply(Optional.ofNullable(accessory.getQty()).orElse(BigDecimal.ZERO)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 计算 math 这个对象 里面的两个 BigDecimal 的 加法
BigDecimal addResult = maths.stream()
// 模拟 过滤操作
.filter(math -> true)
// 加法
.map(accessory -> Optional.ofNullable(accessory.getPrice())
.orElse(BigDecimal.ZERO).add(Optional.ofNullable(accessory.getQty()).orElse(BigDecimal.ZERO)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println(multiplyResult);
System.out.println(addResult);
}
这个例子里面,都要获取到 maths 这个 列表的 Stream ,然后去做不同的操作,这样写会有很多重复代码,改一下代码,两个操作复用一个流的话:
/**
* 计算 BigDecimal 的计算
*
* @author jy
* @date 2022/4/11 14:45:25
*/
public static void main(String[] args) {
ArrayList<Math> maths = new ArrayList<>();
maths.add(new Math(new BigDecimal("1"), new BigDecimal("13")));
maths.add(new Math(new BigDecimal("2"), BigDecimal.TEN));
// 提取出公共代码来
Stream<Math> mathStream = maths.stream()
// 模拟 过滤操作
.filter(math -> true);
// 计算 math 这个对象 里面的两个 BigDecimal 的 乘积
BigDecimal multiplyResult = mathStream
// 乘法
.map(accessory -> Optional.ofNullable(accessory.getPrice())
.orElse(BigDecimal.ZERO).multiply(Optional.ofNullable(accessory.getQty()).orElse(BigDecimal.ZERO)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 计算 math 这个对象 里面的两个 BigDecimal 的 加法
BigDecimal addResult = mathStream
// 加法
.map(accessory -> Optional.ofNullable(accessory.getPrice())
.orElse(BigDecimal.ZERO).add(Optional.ofNullable(accessory.getQty()).orElse(BigDecimal.ZERO)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println(multiplyResult);
System.out.println(addResult);
}
提取出来一个 mathStream ,此时执行第二个使用 mathStream 的时候,就会抛出如下异常:
Stream has already been operated upon or closed 流已经被操作或关闭
说明我们创建的 Stream 只能使用一次,每次要重新创建,可以使用生产者函数式接口 (Supplier)复用流,因为 Lambda 表达式的特性,我们可以将生成 Stream 的操作放在 Supplier 的 Lambda 表达式中,从而复用。
/**
* 计算 BigDecimal 的计算
*
* @author jy
* @date 2022/4/11 14:45:25
*/
public static void main(String[] args) {
ArrayList<Math> maths = new ArrayList<>();
maths.add(new Math(new BigDecimal("1"), new BigDecimal("13")));
maths.add(new Math(new BigDecimal("2"), BigDecimal.TEN));
// 用 lambda 进行封装
Supplier<Stream<Math>> mathSupplier = () -> maths.stream()
// 模拟 过滤操作
.filter(math -> true);
// 计算 math 这个对象 里面的两个 BigDecimal 的 乘积
BigDecimal multiplyResult = mathSupplier.get()
// 乘法
.map(accessory -> Optional.ofNullable(accessory.getPrice())
.orElse(BigDecimal.ZERO).multiply(Optional.ofNullable(accessory.getQty()).orElse(BigDecimal.ZERO)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 计算 math 这个对象 里面的两个 BigDecimal 的 加法
BigDecimal addResult = mathSupplier.get()
// 加法
.map(accessory -> Optional.ofNullable(accessory.getPrice())
.orElse(BigDecimal.ZERO).add(Optional.ofNullable(accessory.getQty()).orElse(BigDecimal.ZERO)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println(multiplyResult);
System.out.println(addResult);
}
通过控制 import * 降低项目打包复杂度
当项目引入了很多同一个包下的类时,idea默认会使用 import * 来注入这个包,会增加打包后的项目体积,在设置中可以设置同一个包使用 import * 注入的限制:
之前我设置的是 5,是这样的
设置成 100 以后,是这样的
MySql中int(10)大小问题
开发中我需要存储一个时间戳在库中,是用来记录延时任务时间间隔的,所以我想使用int(10)存储。
864000000 是 以毫秒为单位 的 10 天,
int 类型的 范围是 232−12^{32}-1232−1 到 −232+1-2^{32} + 1 −232+1
所以当我设置更大值的时候,系统自动帮我设定为了
Lombok 的 @Builder
注解
使用时建议配合
@NoArgsConstructor
和@AllArgsConstructor
使用,就不存在一系列的奇葩问题了
忽略接口返回的某个参数
- 从 swagger 文档中忽略
// 在ApiModelProperty中添加hidden=true
@ApiModelProperty(value = "所属机构", hidden=true)
private String createUserOrgNo;
- 不返回这个字段
// 增加 @JsonIgnore注解
@ApiModelProperty(value = "自增序列")
@JsonIgnore
private Integer seq;
OpenFeign遇到的问题
请求 的压缩最大大小的问题
为查询的对象过大,导致传输数据不全,json反序列化失败
下图属性控制的,改稍微大些即可解决
接口返回列表的时候,Content-Type类型不一致
报错如下:
_Could not extract response: no suitable HttpMessageConverter found for response type [java.util.List<java.util.Ma
p<java.lang.String, java.lang.Object>>] and content type [application/xml;charset=UTF-8]
feign.codec.DecodeException: Could not extract response: no suitable HttpMessageConverter found for response type [java.util.List<java.util.Map<java.lang.String, java.lang.Object>>] and content typ
e [application/xml;charset=UTF-8]
解决办法是将调用方和被调用方的 接口显式指定类型:
被调用方 指定 produces = "application/json;charset=utf-8"
/**
* 获取 orgGuid 下的 userGuid list
*
* @param orgGuid 组织主键
* @return userGuid list
*/
@IgnoreToken
@ApiVersion
@GetMapping(value = "/{version}/queryUserGuidListByOrgGuid", produces = "application/json;charset=utf-8")
@ApiOperation(value = "queryUserGuidListByOrgGuid", httpMethod = "GET", tags = "获取 orgGuid 下的 userGuid list")
public RestResponse<List<String>> queryUserGuidListByOrgGuid(@RequestParam("orgGuid") String orgGuid) {
return RestResponse.ok(organizationService.queryUserGuidListByOrgGuid(orgGuid));
}
调用方指定 produces = "application/json;charset=utf-8"
/**
* 获取 orgGuid 下的 userGuid list
*
* @param orgGuid 组织主键
* @return userGuid list
*/
@GetMapping(value = "/serverapi/organization/v1/queryUserGuidListByOrgGuid", produces = "application/json;charset=utf-8")
RestResponse<List<String>> queryUserGuidListByOrgGuid(@RequestParam("orgGuid") String orgGuid);
Get请求的 @RequestParam
feign调用的 get请求在接口直接加参数的时候需要用该注解,不可不加,否则会抛出异常
参考文章
转载自:https://juejin.cn/post/7140228766944460807