背景&问题
在【财务资金指标录入】项目中,由于对数据库的“修改”操作较为复杂,一条sql语句无法完成整个操作,所以需要在ServiceImpl中人为控制事务的提交和回滚,以保证数据库数据的准确性和一次数据库写操作的原子性。
解决方案
在ServiceImpl中注入Spring框架提供的 DataSourceTransactionManager 对象,使用该对象,并配合try…catch…代码块完成手动事务控制。
代码
ServiceImpl:
package com.sfpay.console.service.impl;
......
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
......
/**
* 类说明:<br>
* 财务资金指标录入模块Service层实现类
* @author 80003614
* CreateDate: 2019-2-11
*/
@Service("finFundIdxService")
public class FinFundIdxServiceImpl implements FinFundIdxService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
// 1.获取事务控制管理器
@Autowired
private DataSourceTransactionManager transactionManager;
@Autowired
private FinFundIdxMapper finFundIdxMapper;
......
// 修改
@Override
public void updateFinFundIdx(FinanceFundIndex finFundIdx) {
// 2.获取事务定义
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 3.设置事务隔离级别,开启新事务
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
// 4.获得事务状态
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 5.具体的数据库操作(多个)
logger.debug("update到数据库前的JavaBean预览:" + finFundIdx.toString());
// "修改"操作需要与数据库进行3次交互,这3次写操作必须放在一个事务中控制!!!
finFundIdxMapper.updateFinFundIdx(finFundIdx);
// int a = 1 / 0;
finFundIdxMapper.updateFinFundIdx2(finFundIdx);
finFundIdxMapper.updateFinFundIdx3(finFundIdx);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw new RuntimeException(e.getMessage());
}
}
}
Mapper.xml:
/**
* 配置说明:<br>
* 财务资金指标录入模块Mapper.xml
* @author 80003614
* CreateDate: 2019-2-11
*/
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sfpay.console.dao.FinFundIdxMapper">
<resultMap id="BaseResultType" type="com.sfpay.console.domain.FinanceFundIndex">
<id column="IDX_ID" property="idxId" jdbcType="DECIMAL" />
<result column="IDX_NAME" property="idxName" jdbcType="VARCHAR" />
<result column="IDX_VAL" property="idxVal" jdbcType="DECIMAL" />
<result column="BEGIN_DATE" property="beginDate" jdbcType="DATE" />
<result column="END_DATE" property="endDate" jdbcType="DATE" />
<result column="CREATE_TIME" property="createTime" jdbcType="DATE" />
<result column="UPDATE_TIME" property="updateTime" jdbcType="DATE" />
</resultMap>
......
<!-- 更新 -->
<!-- 对"财务资金指标"的修改操作需要完成3个步骤,并用同一个事务控制 -->
<!-- 第一步:针对要修改的那条记录作update或insert操作 -->
<update id="updateFinFundIdx" parameterType="com.sfpay.console.domain.FinanceFundIndex" >
merge into EDWSDATA.IMP_FIN_IDX idi
using (
select distinct IDX_ID, IDX_NAME, #{idxVal,jdbcType=DECIMAL} IDX_VAL,
#{beginDate,jdbcType=DATE} BEGIN_DATE from EDWSDATA.IMP_FIN_IDX
where IDX_ID = #{idxId,jdbcType=DECIMAL}
) t
on (t.IDX_ID = idi.IDX_ID and t.BEGIN_DATE = idi.BEGIN_DATE)
when matched then update
set idi.IDX_VAL = t.IDX_VAL, idi.END_DATE = to_date('2099-12-31','yyyy-mm-dd'), idi.UPDATE_TIME = sysdate
when not matched then insert(IDX_ID, IDX_NAME, IDX_VAL, BEGIN_DATE, END_DATE, UPDATE_TIME)
values(t.IDX_ID, t.IDX_NAME, t.IDX_VAL, t.BEGIN_DATE, to_date('2099-12-31','yyyy-mm-dd'), sysdate)
</update>
<!-- 第二步:删除这条记录后面的所有记录(以begin_date为基准) -->
<delete id="updateFinFundIdx2" parameterType="com.sfpay.console.domain.FinanceFundIndex" >
delete from EDWSDATA.IMP_FIN_IDX t
where t.IDX_ID = #{idxId,jdbcType=DECIMAL}
and t.BEGIN_DATE > #{beginDate,jdbcType=DATE}
</delete>
<!-- 第三步:修改被修改记录的前一条记录的end_date值 -->
<update id="updateFinFundIdx3" parameterType="com.sfpay.console.domain.FinanceFundIndex" >
update EDWSDATA.IMP_FIN_IDX t
set t.END_DATE = #{beginDate,jdbcType=DATE}
where t.IDX_ID = #{idxId,jdbcType=DECIMAL}
and t.BEGIN_DATE =
(select BEGIN_DATE from
(select BEGIN_DATE, ROW_NUMBER() OVER (PARTITION BY IDX_ID ORDER BY BEGIN_DATE DESC) as rank
from EDWSDATA.IMP_FIN_IDX where IDX_ID = #{idxId,jdbcType=DECIMAL}) t1 where t1.rank = 2)
</update>
</mapper>