码迷,mamicode.com
首页 > 其他好文 > 详细

记录一次bug解决过程:规范变量名称和mybatis的使用以及代码优化

时间:2016-08-05 22:48:42      阅读:361      评论:0      收藏:0      [点我收藏+]

标签:

一、总结

  1. Mybatis中当parameterType为基本数据类型的时候,统一采用_parameter来代替基本数据类型变量。
  2. Mybatis中resultMap返回一个对象,resultType返回一个Map简单数据类型(由于需要缓存到JVM中)的映射关系。
  3. String类型转Integer类型;String类型转int类型用到的方法是不一样的。
  4. 方法入口处第一行写new Date(),防止时间在23:59:59跨界对逻辑带来影响。
  5. 考虑到上线app_resource表忘记配置供应商比例,在代码中逻辑中注意要加入空指针判断,增强代码健壮性。
  6. 核心代码处要加注释,关键代码处要打日志,业务逻辑执行失败要考虑是否需要告警邮件。
  7. 变量命名要规范;测试工单的工单标题命名要规范。
  8. 代码逻辑中有if使用的地方,尽量想想else使用的场景,保证逻辑严谨性。
  9. VPN软件的使用;热部署的使用(http://docs.alibaba-inc.com/)。

二、Bug描述:Mybatis中parameterType使用

mapper层中使用parameterType="java.lang.Integer"基本类型,代码报错:

//org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: 
//  There is no getter for property named ‘siteId‘ in ‘class java.lang.Integer‘

解决办法,当入参为基本数据类型的使用,使用_parameter代替基本数据类型,如下:

  <select id="getRulesInfoBysiteId" parameterType="java.lang.Integer" resultMap="RulesMap" >
          SELECT
            a.site_id,
            a.site_name,
            b.id AS city_id,
            b.`name` AS city_name,
            c.id AS region_id,
            c.`name` AS region_name
        FROM
            idc_site a,
            city b,
            area c
        WHERE
            a.region = c.`name`
        AND a.city = b.`name`
        AND a.is_deleted = ‘n‘
        AND b.is_deleted = ‘n‘
        AND c.is_deleted = ‘n‘
        <if test="_parameter != null">
            AND a.site_id = #{_parameter,jdbcType=INTEGER}
        </if>
  </select>

或者在mapper层的接口中,给这个方法的参数加上@param(value=“siteId”),这样就能在.xml中使用#{siteId,jdbcType=INTEGER}了,仅使用于基本数据类型

//mapper层对应的接口中必须加@Resource注解,否则在Dao层注入*Ext会失败
@Resource public interface SiteMapperExt extends SiteMapper { //mapper层对应的接口中加mybatis提供的注解@Param("siteId") public RulesInfo getRulesInfoBysiteId(@Param("siteId")Integer siteId); }

更多使用详情请看最后附文。

三、Bug描述:

    /**
     * 自动分配物流供应商
     */
    @Override
    public void autoAssignSupplier(RuleInfos ruleInfos, String deviceType, WorkOrderMain workOrder, int amounts) {

        // 精确匹配规则制定 物流供应商
        LogisticsAssignRules bean = getExactMatchSPId(ruleInfos.getSourceRegionId(), ruleInfos.getTargetRegionId(),
                                                      ruleInfos.getSourceCityId(), ruleInfos.getTargetCityId(),
                                                      ruleInfos.getSourceSiteName(), ruleInfos.getTargetSiteName(),
                                                      deviceType, amounts);
        if (null == bean) {
            // 按比例规则制定物流供应商
            Map<String, String> supplierRatesMap = getSpRates();
            Map<String, String> logicOf90DaysBefore = getAssignRates();
            String supplierId = getSupplierIdBy90Days(supplierRatesMap, logicOf90DaysBefore, amounts);
            logisticsWorkOrderBo.LogisticsAssigned(workOrder, WorkOrderStatsCst.LogisticsOrderState.unassigned,
                                                   supplierId, getSpRatesDesc(), WorkOrderCst.DEFAULT_VALUE_YES);
            logger.info("auto assign supplier as rates,supplierId = {}, description = {}", supplierId, getSpRatesDesc());
        } else {
            // 精确匹配,直接分配物流供应商
            logisticsWorkOrderBo.LogisticsAssigned(workOrder, WorkOrderStatsCst.LogisticsOrderState.unassigned,
                                                   bean.getSpId().toString(), bean.getRuleJsonVal(),
                                                   WorkOrderCst.DEFAULT_VALUE_YES);
            logger.info("auto assign supplier start as rules, supplierId = {}, description = {}",
                        bean.getSpId().toString(), getSpRatesDesc());
        }
    }

在接口调用中,当传递属性过多的时候,可以考虑用对象来传递,方便以后的扩展。如本代码中,当后续添加规则时,需要更新方法。另外对于公用的东西,尽量维护在静态枚举值中。

四、Bug描述:方法入口处统一获取当前时间new Date()

在代码中的时间要作为条件来筛选数据,如果同一个方法中,在多个地方出现new Date(),算上程序执行的纳秒级别的时间,可能在当前日期的“23:59:59 纳秒”产生跨界时间的问题,给代码造成概率极低的隐患。

SELECT
    d.sp_id,
    COUNT(a.sn) AS asset_counts
FROM
    idc_asset_list a
LEFT JOIN idc_work_order_main b ON a.order_id = b.id
LEFT JOIN idc_order_atomic_list c ON c.order_id = b.id
LEFT JOIN idc_atomic_logistics d ON d.atomic_id = c.atomic_id
WHERE
    a.is_deleted = n
AND b.is_deleted = n
AND c.is_deleted = n
AND d.is_deleted = n
AND d.sp_id IS NOT NULL
AND b.gmt_create < CONCAT(2016-08-04, 23:59:59)
AND b.gmt_create > date_sub(
    2016-08-04 00:00:00,
    INTERVAL 3 MONTH
)
AND (
    b.state != cancle
    OR b.sub_state != cancle
)
GROUP BY
    d.sp_id
ORDER BY
    sp_id DESC

因为要将上述数据缓存到JVM中,数据结构在集群中的一台机器上只维护一份。一天最多查询8次。

技术分享

使用到的SQL如下:

    <select id = "getLogisticsList90DaysBefore" parameterType="java.lang.String" resultType ="java.util.Map">
            SELECT
                d.sp_id AS spId,
                COUNT(a.sn) AS assetCounts
            FROM
                idc_asset_list a
            LEFT JOIN idc_work_order_main b ON a.order_id = b.id
            LEFT JOIN idc_order_atomic_list c ON c.order_id = b.id
            LEFT JOIN idc_atomic_logistics d ON d.atomic_id = c.atomic_id
            WHERE
                a.is_deleted = n
            AND b.is_deleted = n
            AND c.is_deleted = n
            AND d.is_deleted = n
            AND d.sp_id IS NOT NULL
            AND (b.state != cancle OR b.sub_state != cancle)
            <if test = "_parameter != null  and _parameter !=‘‘">
                AND a.gmt_create &lt;= CONCAT(#{yesterday}, 23:59:59)
                AND a.gmt_create &gt;= DATE_SUB(CONCAT(#{yesterday}, 00:00:00), INTERVAL 3 MONTH)
            </if>
            GROUP BY
                d.sp_id
            ORDER BY
                sp_id DESC
    </select>

mapper层的代码中,我们使用了mysql函数date_sub(concat(""), interval 3 month),并且返回resultType="java.util.Map",我们使用结构List<String,Map<String,Object>>结构来接收查询结果,而没有采用resultMap封装对象来接收结果。

技术分享

SQL执行之后的返回结果为list,通过断点跟踪获悉sp_id为Integer类型,asset_counts为Long类型。

//获取spId
Integer spId = map.get("spId");
//获取assetCounts
Long assetCounts = map.get("assetCounts");

故使用如下代码获取查询结果,但是代码中封装了数据类型,所以统一采用Object来获取。

五、Bug描述:考虑到线上缺失配置文件,添加空指针判断;为程序健壮性,必须在前后端同时对参数完整性作出校验。

    /**
     * 校验参数的完整性 {设备类型与数量必填,用于规则匹配校验}
     */
    private void checkParameters(AssignSupplierRulesDTO dto) {
        // 数量合理性校验
        if (StringUtils.isNotBlank(dto.getAssetNum())) {
            if (dto.getAssetNum().toCharArray().length <= 1) {
                throw new ServiceException(ErrorCode.Params_Lost);
            } else {
                if (!(StringUtils.isNumeric(dto.getAssetNum().substring(1)))) {
                    throw new ServiceException(ErrorCode.Params_Invalid);
                }
                if (!("><=≤≥≠".contains(dto.getAssetNum().substring(0, 1)))) {
                    throw new ServiceException(ErrorCode.Params_Invalid);
                }
            }
        }
        // 供应商必填
        if (null == dto.getSpId()) {
            throw new ServiceException(ErrorCode.Params_Lost);
        }
        // 规则名称必填
        if (StringUtils.isBlank(dto.getRuleName())) {
            throw new ServiceException(ErrorCode.Params_Lost);
        }

        // 当指定规则类型的时候,关联性校验
        if (StringUtils.isNotBlank(dto.getRuleType())) {
            // 同城校验
            if (dto.getRuleType().equals(WorkOrderCst.RelocationType.SameCity.name())) {
                if (!(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getSourceCity()))
                    && !(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getSourceCity()))) {
                    if (dto.getSourceCity() != dto.getTargetCity()) {
                        throw new ServiceException(ErrorCode.Params_Invalid);
                    }
                }
            }
            // 同区域内校验
            if (dto.getRuleType().equals(WorkOrderCst.RelocationType.RegionalIn)) {

                if (!(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getSourceRegion()))
                    && !(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getTargetRegion()))) {
                    if (StringUtils.isNotBlank(dto.getSourceRegion()) && StringUtils.isNotBlank(dto.getTargetRegion())) {
                        // 区域必须相等
                        if (!(dto.getSourceRegion().equals(dto.getTargetRegion()))) {
                            throw new ServiceException(ErrorCode.Params_Invalid);
                        }
                    }
                }
                if (!(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getSourceCity()))
                    && !(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getTargetCity()))) {
                    if (StringUtils.isNotBlank(dto.getSourceCity()) && StringUtils.isNotBlank(dto.getTargetCity())) {
                        if (!addressBo.whetherCityInTheSameArea(dto.getSourceCity(), dto.getTargetCity())) {
                            throw new ServiceException(ErrorCode.Params_Invalid);
                        }
                    }
                }
                if (!(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getSourceSite()))
                    && !(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getTargetSite()))) {
                    if (StringUtils.isNotBlank(dto.getSourceSite()) && StringUtils.isNotBlank(dto.getTargetSite())) {
                        if (!addressBo.whetherSiteInTheSameArea(dto.getSourceSite(), dto.getTargetSite())) {
                            throw new ServiceException(ErrorCode.Params_Invalid);
                        }
                    }
                }
            }
            // 不同区域的校验
            if (dto.getRuleType().equals(WorkOrderCst.RelocationType.RegionalOut)) {
                if (!(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getSourceRegion()))
                    && !(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getTargetRegion()))) {
                    if (StringUtils.isNotBlank(dto.getSourceRegion()) && StringUtils.isNotBlank(dto.getTargetRegion())) {
                        if (dto.getSourceRegion().equals(dto.getTargetRegion())) {
                            throw new ServiceException(ErrorCode.Params_Invalid);
                        }
                    }
                }
                if (!(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getSourceCity()))
                    && !(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getTargetCity()))) {
                    if (StringUtils.isNotBlank(dto.getSourceCity()) && StringUtils.isNotBlank(dto.getTargetCity())) {
                        if (addressBo.whetherCityInTheSameArea(dto.getSourceCity(), dto.getTargetCity())) {
                            throw new ServiceException(ErrorCode.Params_Invalid);
                        }
                    }
                }
                if (!(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getSourceSite()))
                    && !(WorkOrderCst.MATCH_ALL_PARAMETERS.equals(dto.getTargetSite()))) {
                    if (StringUtils.isNotBlank(dto.getSourceSite()) && StringUtils.isNotBlank(dto.getTargetSite())) {
                        if (addressBo.whetherSiteInTheSameArea(dto.getSourceSite(), dto.getTargetSite())) {
                            throw new ServiceException(ErrorCode.Params_Invalid);
                        }
                    }
                }
            }
        }
    }

六、Bug描述:String转Integer;String转int的熟练使用。

public class Test {
    public static void main(String[] args) {
        String number = "520";
        Integer a = 521;
        int b = 522;
        
        //String转Integer
        Integer.valueOf(number);
        
        //String转int
        Integer.parseInt(number);
        new Integer(number).intValue();
        
        //Integer转String
        a.toString();
        
        //Integer转int
        a.intValue();
        
        //int转String
        String.valueOf(b);
        Integer.toString(b);
        String str = "" + b;
        
        //int转Integer
        new Integer(b);
        
        //String转BigDecimal
        new BigDecimal(number);
        //获取今天日期
        new Date(System.currentTimeMillis()); // Fri Aug 05 20:16:07 CST 2016
        DateFormat.getDateInstance().format(new Date()); // 2016-8-5 
    }
}

七、List和数组的转换

public class Test {
    public static void main(String[] args) {
        String[] family = { "XuG", "XuX", "GaiZ", "LianW" };
        List<String> list = new ArrayList<String>(Arrays.asList("XuG", "XuX", "GaiZ", "LianW"));
        
        //数组转list
        List<String> list_01 = new ArrayList<String>(Arrays.asList(family));
        
        //list转数组
        String[] str = (String[])list.toArray();
    }
}

八、Bug描述:变量命名规范。

技术分享

 

变量的命名规范要有意义,在数据库建表,创建java bean的时候,一定要保证单词使用的正确性。如label和lable;region和regin。要注意到变量的命名可能跟数据库的关键字或java的关键字有冲突,可以采用下划线的原则处理关键字冲突

九、Bug描述:逻辑严谨性。

private String getSupplierIdBy90Days(Map<String, String> supplierRatesMap, Map<String, String> logicOf90DaysBefore,
                                         int dispatchNum) {
        int ratesCount = 0, dispatchCount = 0;
        for (String spId : supplierRatesMap.keySet()) {
            ratesCount = ratesCount + Integer.parseInt(supplierRatesMap.get(spId));
        }
        for (String spId : logicOf90DaysBefore.keySet()) {
            dispatchCount = dispatchCount + Integer.parseInt(logicOf90DaysBefore.get(spId));
        }
        Map<String, String> idealizedMap = new HashMap<String, String>();
        for (String spId : supplierRatesMap.keySet()) {
            Integer dispathNum = (dispatchCount * Integer.parseInt(supplierRatesMap.get(spId))) / ratesCount;
            idealizedMap.put(spId, dispathNum.toString());
        }
        int gap = -1;
        String supplierId = StringUtils.EMPTY;
        if (CollectionUtils.isNotEmpty(logicOf90DaysBefore.keySet())) {
            for (String spId : logicOf90DaysBefore.keySet()) {
                if (null != idealizedMap.get(spId)) {
                    int mix = Integer.parseInt(idealizedMap.get(spId))
                              - Integer.parseInt(logicOf90DaysBefore.get(spId));
                    if (mix < gap) {
                        gap = mix;
                        supplierId = spId;
                    }
                } else {
                    supplierId = spId; // 新添加的供应商比例
                }
            }
        } else {
            supplierId = new ArrayList<String>(supplierRatesMap.keySet()).get(0);
        }
        return supplierId;
    }

  有判断if条件的地方,要考虑到else的可能出现情况,尤其是if else 嵌套多层的时候,可能某些else的情况遗漏,会给程序带来问题。如上述代码中的else的缺失,可能在“新添加供应商比例”的情况下,出现没有分配供应商的情况。

十、VPN工具

  VPN工具下载使用:Cisco AnyConnect VPN Client 64位下载

记录一次bug解决过程:规范变量名称和mybatis的使用以及代码优化

标签:

原文地址:http://www.cnblogs.com/RunForLove/p/5742797.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!