2015年12月

本周测试同事,在测试我做的一个需求的时候,发现了这么一个bug。
CDMS下发下游系统的数据中,有几条数据有问题,因为其中两个字段是有关联的,都是从同一个字段扩展来的,明眼一看就有问题。
可是这几条数据,都是在cursor里处理的,不会出现,前一个字段扩展用的值是一个样,后一个字段扩展用的是另一个。
我就很费解,并且我在开发环境无法重现。我试了几组数据,下发都没有问题。
然后我就开始检查测试环境出错的数据,发现了猫腻。
现在我就拿Java代码模拟一下这个BUG。

下发下游的数据为Security,如下类所示,我简单的建了三个字段。

package com.chengjf.fxdemo;

public class Security {
    // 证券ID
    private String securityID;
    // 证券名称
    private String securityName;
    // 证券市场
    private String market;

    public Security(String securityID, String securityName, String market) {
        this.securityID = securityID;
        this.securityName = securityName;
        this.market = market;
    }

    public String getSecurityID() {
        return securityID;
    }

    public void setSecurityID(String securityID) {
        this.securityID = securityID;
    }

    public String getSecurityName() {
        return securityName;
    }

    public void setSecurityName(String securityName) {
        this.securityName = securityName;
    }

    public String getMarket() {
        return market;
    }

    public void setMarket(String market) {
        this.market = market;
    }

}

下发逻辑如下:

public class OracleException {

    // 模拟数据库
    private Map<String, String> db = new HashMap<String, String>(8);

    public static void main(String[] args) {

        OracleException exception = new OracleException();

        Collection<Security> collection = getSecurities();
        Iterator<Security> iterator = collection.iterator();

        // 要插入数据库的字段
        String systemID = null;
        String tradeID = null;

        while (iterator.hasNext()) {
            Security security = iterator.next();
            try {
                // 对systemID进行处理
                // !这块处理的时候,某些数据不符合条件,导致没有生成systemID
                // !而是用上一次处理生成的ID
                if ("1".equals(security.getMarket())) {
                    systemID = security.getSecurityID() + "_SYSTEM";
                }
                // 对tradeID继续处理
                tradeID = security.getSecurityID() + "_TRADE";

                // 插入数据库
                exception.insertIntoDB(systemID, tradeID);

            } catch (IllegalArgumentException e) {
                System.err.println(e);
            }
        }

        // 输出数据库结果
        System.out.println(exception.db);
        /*        
         * 000002_SYSTEM=600002_TRADE, 
         * 000001_SYSTEM=000001_TRADE, 
         * 000003_SYSTEM=000003_TRADE, 
         * 000004_SYSTEM=600004_TRADE
         * 000002竟然对应600002,00004竟然对应600004
         * 显然错误
         */
    }

    /**
     * 插入数据库,如果systemID没有则插入,有则更新 借助Map结构的特性
     * 
     * @param systemID
     * @param tradeID
     */
    private void insertIntoDB(String systemID, String tradeID) {
        if (systemID != null &amp;&amp; tradeID != null) {
            this.db.put(systemID, tradeID);
        } else {
            throw new IllegalArgumentException();
        }
    }

    /**
     * 数据源Cursor
     * 
     * @return
     */
    private static Collection<Security> getSecurities() {
        Security security1 = new Security("000001", "平安银行", "1");
        Security security2 = new Security("000002", "万科A", "1");
        Security security3 = new Security("600001", "邯郸钢铁", "2");
        Security security4 = new Security("600002", "齐鲁石化", "2");
        Security security5 = new Security("000003", "PT金田A", "1");
        Security security6 = new Security("000004", "国农科技", "1");
        Security security7 = new Security("600003", "ST东北高", "2");
        Security security8 = new Security("600004", "白云机场", "2");

        return Arrays.asList(new Security[] { security1, security2, security3,
                security4, security5, security6, security7, security8 });
    }
}

出现BUG原因的,就是在对数据进行处理的时候,没有对每条数据的context进行初始化。下一条数据因为一些逻辑,某些值竟然和上一条数据的某些值相同或者有关系。最终造成了数据不一致的结果。

然而,我发现这种代码在系统中很多,特别是在PL/SQL中。一般变量只在begin开头初始化,每次cursor的loop里都没有重新初始化,都有这种BUG的隐患。

同时,这种BUG应该是最简单的一个BUG,但是可能也是最容易犯的。这种BUG发生的时候跟处理的数据有关系,一般数据正常的时候不会出现这种BUG,但是数据如果有问题,就不灵了。

这种BUG不容易发现,但是应该谨记:

数据初始化非常重要。保证数据处理时的context一切正常。