欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-13 08:07:34
...

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