记一次新建操作(insert)的优化过程

1.插入操作的sql语句

Mysql的 insert常用方法——插入后返回插入成功后的主键

 <operation name="insert" paramtype="object">
        <sql>
            <![CDATA[
                INSERT INTO ucdp_budget_plan
                (gmt_create,gmt_modified,biz_date,status,name,biz_code,scene_code,lasted_operator,plan_type,bu_info,total,budget_elements,assign_config,notify_time,digest,environment)
                VALUES
                (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
            ]]>
        </sql>
        <sqlmap>
            <![CDATA[
				INSERT INTO ucdp_budget_plan(gmt_create,gmt_modified,biz_date,status,name,biz_code,scene_code,lasted_operator,plan_type,bu_info,total,budget_elements,assign_config,notify_time,digest,environment)
                VALUES(CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, #bizDate#, #status#,#name#, #bizCode#, #sceneCode#, #lastedOperator#, #planType#,#buInfo#,#total#, #budgetElements#, #assignConfig#, #notifyTime#,#digest#,#environment#)
				<selectKey resultClass="long" keyProperty="id" type="post">
				SELECT LAST_INSERT_ID()
				</selectKey>
			]]>
        </sqlmap>
    </operation>

上面的方法利用的是mysql的LAST_INSERT_ID()函数会返回当前连接上次插入记录的自增主键。

2.Repo层的一写多读模式,层层优化

  • 最原始语句
  public long insert(BudgetPlanBO budgetPlanBO) {
     
        LoggerUtil.debug(LOGGER, "insert", budgetPlanBO);
        return ucdpBudgetPlanDAO.insert(BudgetPlanConvertor.convertBOtoDO(budgetPlanBO));
    }
  • 添加唯一索引防重插功能
    public long insert(BudgetPlanBO budgetPlanBO) {
     
        LoggerUtil.debug(LOGGER, "insert", budgetPlanBO);
        try {
     
            return ucdpBudgetPlanDAO.insert(BudgetPlanConvertor.convertBOtoDO(budgetPlanBO));
        } catch (DuplicateKeyException e) {
     
            return ucdpBudgetPlanDAO.queryByUniqueId(budgetPlanBO.getPlanType().name(), budgetPlanBO.getBizDate(),
                    budgetPlanBO.getBizCode()).getId();
        }
    }

以上的日志打印存在严重问题,不仅在线上环境打不了日志,而且打出来的日志没有具体参数,没有排查的实际帮助意义。

  • 优化日志版
     public long insert(BudgetPlanBO budgetPlanBO) {
     
         LoggerUtil.info(LOGGER, "[createBudget]插入预算:{0}", budgetPlanBO);
        try {
     
            //same as above.
        } catch (DuplicateKeyException e) {
     
            //same as above.
        }
    }

存在两个问题:
以上语句只是一次创建,在实际项目中一个接口要进行200+次的批量创建。开发环境没问题,但在预发环境中批量创建出现了部分成功部分失败的问题。唯一索引的搜索,使得本语句变成了一写多读的情况。就会导致一直返回0,因为写连接跟读连接不是同一个,解决方法是通过开启事务,强制使用同一连接进行读写。除非一定要获取插入id的情况,否则不推荐使用事务来获取插入id。

  • 添加事务版
public long insert(BudgetPlanBO budgetPlanBO) {
     
        LoggerUtil.info(LOGGER, "[createBudget]插入预算:{0}", budgetPlanBO);
        return this.ucdpMngTransactionTemplate.execute(new TransactionCallback<Long>() {
     
            @Override
            public Long doInTransaction(TransactionStatus transactionStatus) {
     
                try {
     
                    return ucdpBudgetPlanDAO.insert(BudgetPlanConvertor.convertBOtoDO(budgetPlanBO));
                } catch (DuplicateKeyException e) {
     
                    return ucdpBudgetPlanDAO.queryByUniqueId(budgetPlanBO.getPlanType().name(), budgetPlanBO.getBizDate(),
                            budgetPlanBO.getBizCode()).getId();
                } catch (Throwable e) {
     
                    LoggerUtil.error(LOGGER, "[insert]插入预算异常, Exception={0}", JSON.toJSONString(e));
                    transactionStatus.setRollbackOnly();
                    throw new UcdpMngException(e.getMessage());
                }
            }
        });
    }

你可能感兴趣的:(BugKill)