错误描述
一个新的应用在更新表(mysql)数据时,出现异常。
异常日志:
java.sql.SQLIntegrityConstraintViolationException: Column 'total_delivery_quantity_of_current_month' cannot be null
即有null值 对表进行更新 或插入,而该字段在mysql的定义是 非null。
问题已经定位,开始定位问题产生的原因:
找到执行异常的类:
if (org.apache.commons.lang3.StringUtils.isBlank(updateLastDeliveryCommand.getCode())) {
throw new ServiceException("编码不能为空!");
}
LambdaUpdateWrapper<CrmDeliveryQuantityInfoEntity> wrapper = new LambdaUpdateWrapper<>();
wrapper.set(CrmDeliveryQuantityInfoEntity::getDeliveryQuantityRatioOf7Days, updateLastDeliveryCommand.getDeliveryQuantityRatioOf7Days());
wrapper.set(CrmDeliveryQuantityInfoEntity::getTotalDeliveryQuantityOfCurrentMonth, updateLastDeliveryCommand.getTotalDeliveryQuantityOfCurrentMonth());
wrapper.set(CrmDeliveryQuantityInfoEntity::getTotalDeliveryQuantityOfPreviousMonth, updateLastDeliveryCommand.getTotalDeliveryQuantityOfPreviousMonth());
wrapper.set(CrmDeliveryQuantityInfoEntity::getDayAvgDeliveryQuantityOfPreviousMonth, updateLastDeliveryCommand.getDayAvgDeliveryQuantityOfPreviousMonth());
wrapper.set(CrmDeliveryQuantityInfoEntity::getMonthDeliveryQuantityOfSiteRatio, updateLastDeliveryCommand.getMonthDeliveryQuantityOfSiteRatio());
wrapper.set(CrmDeliveryQuantityInfoEntity::getMonthDeliveryQuantityOfManagerAreaRatio, updateLastDeliveryCommand.getMonthDeliveryQuantityOfManagerAreaRatio());
wrapper.set(CrmDeliveryQuantityInfoEntity::getMonthDeliveryQuantityOfTransferRatio, updateLastDeliveryCommand.getMonthDeliveryQuantityOfTransferRatio());
wrapper.set(CrmDeliveryQuantityInfoEntity::getTotalDeliveryQuantityOf7Days, updateLastDeliveryCommand.getTotalDeliveryQuantityOf7Days());
wrapper.set(CrmDeliveryQuantityInfoEntity::getDeliveryRateOfDecline, updateLastDeliveryCommand.getDeliveryRateOfDecline());
wrapper.eq(CrmDeliveryQuantityInfoEntity::getCode, updateLastDeliveryCommand.getCode());
update(wrapper);
使用mybatis-plus 的 update 进行更新,其中空字段是 deliveryRateOfDecline。
解决思路
1、 在wrapper.set 的时候进行非空判定,如果是null, 不进行set。
但是这种做法又臭又长。
2、mysql-plus 本身应该有类似 Mapper.updateByPrimaryKeySelective() 的API,
在生成sql 语句时,只拼接非空的参数。
选用第二种方法,开始寻找方案。通过mybatis-plus 的主站 注解 | MyBatis-Plus,
FieldStrategy(opens new window)
值 | 描述 |
---|---|
IGNORED | 忽略判断 |
NOT_NULL | 非 NULL 判断 |
NOT_EMPTY | 非空判断(只对字符串类型字段,其他类型字段依然为非 NULL 判断) |
DEFAULT | 追随全局配置 |
NEVER | 不加入SQL |
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableField {
String value() default "";
boolean exist() default true;
String condition() default "";
String update() default "";
FieldStrategy insertStrategy() default FieldStrategy.DEFAULT;
FieldStrategy updateStrategy() default FieldStrategy.DEFAULT;
FieldStrategy whereStrategy() default FieldStrategy.DEFAULT;
FieldFill fill() default FieldFill.DEFAULT;
boolean select() default true;
boolean keepGlobalFormat() default false;
String property() default "";
JdbcType jdbcType() default JdbcType.UNDEFINED;
Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
boolean javaType() default false;
String numericScale() default "";
}
@TableField(value = "delivery_rate_of_decline" , updateStrategy = FieldStrategy.NOT_EMPTY)
private BigDecimal deliveryRateOfDecline;
该注解可以直接配置字段上,在执行更新操作时,忽略 null 或 空 字符串。
配置完成后,进行调试。发现确实能忽略null 。但是产生了新的,问题我不想在每个字段上都增加配置,这个工作量也很大。还时想找到 Mapper.updateByPrimaryKeySelective() 的替代方案。
在csdn 发现一篇新的文章 Mybatis-Plus字段策略FieldStrategy详解_斗者_2013的博客-CSDN博客_fieldstrategy
所有字段 更新策略 默认是 DEFAULT,即跟随全局配置,而全局配置默认是 NOT_NULL。
那按理来说是在使用 service.update 的时候应该会对 null 的字段进行过滤。
仔细核对后发现,文章里面使用的都是 updateById,而不是 update ,增加单元测试进行尝试
// code是主键,所有使用code进行更新
CrmDeliveryQuantityInfoEntity entity = new CrmDeliveryQuantityInfoEntity();
entity.setCode(updateLastDeliveryCommand.getCode());
entity.setDeliveryRateOfDecline(null);
entity.setDeliveryQuantityRatioOf7Days(new BigDecimal(22));
updateById(entity);
生成的sql 语句如下
==> Preparing: UPDATE crm_delivery_quantity_info SET delivery_quantity_ratio_of_7_days=? WHERE code=?
[2022-12-13 20:04:22.499]
DEBUG ==> Parameters: 22(BigDecimal), DM221213008001(String)
成功~~~,但现在仍然不知道为什么update 和 updateById 在执行上有啥区别