记录一个有意思的问题……

Posted 阿拉丁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记录一个有意思的问题……相关的知识,希望对你有一定的参考价值。

今天业务开发出现了一个有意思的问题:

一段操作逻辑里,两次update同一个表,如果两次updated偶成功,则对另一个表进行一次insert操作
两次update是用的同一个方法,传入参数对象属性值不同。

现象:

a.数据库里数据更新操作成功
b.insert操作,时而成功时而失败。
c.如果从头debug跟进每个处理逻辑,插入数据一定处理成功。

处理逻辑
private void deal(){
    DataBO dataBO = new DataBO();
    dataBO.setId(23);
    dataBO.setXxx("XXX");
    dataBo.setYyy("YYY");
    boolean result = update(dataBO);
    if(!result)
        return;

    dataBO = new DataBO();
    dataBO.setId(23);
    dataBO.setOneStatus(0);
    dataBO.setTwoStatus(0);
    result = update(dataBO);
    if(!result)
        return;

    //插入数据
    insert();
}
update方法
public boolean update(DataBO dataBO){
    return dataDAO.updateById(dataBO) > 0;
}
BO
public class DataBO{
    private Long id;
    private int isDeleted;
    private int oneStatus;
    private int twoStatus;
    private String xxx;
    private String yyy;
    private Date gmtCreated;
    private Date gmtModified;
}
mapper
<update id="updateById" parameterClass="DataBO" >
        update data_table set gmt_modified = now()
        <dynamic prepend="" >
             <isNotEmpty prepend="," property="gmtCreated">
                    gmt_created = #gmtCreated#
            </isNotEmpty>
             <isNotEmpty prepend="," property="gmtModified">
                    gmt_modified = #gmtModified#
            </isNotEmpty>
             <isNotEmpty prepend="," property="isDeleted">
                    is_deleted = #isDeleted#
            </isNotEmpty>
            <isNotEmpty prepend="," property="xxx">
                    xxx = #xxx#
            </isNotEmpty>
            <isNotEmpty prepend="," property="yyy">
                    yyy = #yyy#
            </isNotEmpty>
            <isNotEmpty prepend="," property="oneStatus">
                    one_status = #oneStatus#
            </isNotEmpty>
            <isNotEmpty prepend="," property="twoStatus">
                    two_status = #twoStatus#
            </isNotEmpty>
        </dynamic>
    where id = #id#
</update>·

分析定位

表象是,数据库里数据更新明明操作成功了,但插入失败了。
Debug无法复现,就打印了些日志来分析,结果是第二次更新没有成功,影响条数为0
copy日志中的sql去数据库客户端执行,操作成功:

update data_table set gmt_modified = now() ,one_status =0,two_status=0,where id = 23;

所以核心问题就是为什么程序里执行更新操作会时常失败。

排查发现:

  • 数据库update操作,当原数据库记录数据与传入参数没有任何变化时,不执行操作,影响记录数为0
  • 传入参数两个int属性,初始化时会被置为0,导致第二个update需要更新的字段都与数据库记录一致,isNotEmpty条件判断无意义
  • 成功失败的关键,在于now()的取值,若两次更新时间一样,则所有参数一致,更新失败。所以在客户端操作更新成功,debug拉开了时间差异,也会更新成功

这样一段逻辑,到处都是坑,但就是各种错误写法/用法聚集一起,才露出了这种“大新闻”的表象,只要一个条件不符合,这个问题的复杂程度都要降低很多,更容易排查。
比如,属性值类型不用int,或者检查方式更严谨,或者没有gmt_modified = now() 这个条件(哪怕是复制到客户端测试的时候没有这个条件,问题也都早就暴露出来了)……

以上是关于记录一个有意思的问题……的主要内容,如果未能解决你的问题,请参考以下文章

用于从 cloudkit 检索单列的代码模式/片段

CSP核心代码片段记录

前端防扒代码片段

前端防扒代码片段

前端防扒代码片段

前端防扒代码片段