事务场景:需要做一个跑批进行一个流程的实现,0.获取异常数据(多条),for循环对单条数据进行下面三个操作:1).调用别的系统进行查询,2).查询自己的库中数据,3)更改自己数据库中的数据状态。此时就需要对单条数据操作的3步骤进行事务控制,使用的过程中发现两个解决办法:第一:在controller层进数据的获取,for循环处理单条数据时调用service层进行事务的控制.第二:在service层进行事务的手动提交和回滚。(for循环需要注意:事务的开启需要在for循环中,否则会报多次提交事务:do not commit or rollback more than once per transaction)
下面贴代码:第一种:在controller层进数据的获取,for循环处理单条数据时调用service层进行事务的控制、
@Controller
public class TestController {
@Resource
private IClearNoUnfreezeBs iClearNoUnfreezeBs;
//逻辑放到controller中,service进行事务控制start
@RequestMapping("/freeze")
public void testfreeze() throws Exception{
try {
//1.取数据
ist<String> policyList1 = iClearNoUnfreezeBs.getunfreezedate();
//2.for循环处理每条数据
for (String policyno : policyList1) {
n++;
try {
//需要进行事务控制的放到service层进行处理
iClearNoUnfreezeBs.bs_clearNoUnfreeze( policyno,n );
} catch (Exception e) {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
第二:在service层进行事务的手动提交和回滚
@Service
public class ClearNoUnfreezeBs implements IClearNoUnfreezeBs {
private static Logger log = Logger.getLogger(ClearNoUnfreezeBs.class);
@Resource
private IClearNoUnfreezeDao iClearNoUnfreezeDao;
@Resource
private IWechatDao wechatDao;
String rownum = ConfigureUtil.getInstance().getString("rownum");// 访问地址
//获取上月的当前时间
Timestamp lastMonth = GetTimeUtil.getLastMonthTime();
// 1).取出上月末之前承保且尚未做解冻处理的异常保单
List<String> policyList1 = iClearNoUnfreezeDao.findNoUnfreezePolicyno(lastMonth,rownum);
// 2)将list1中的openid遍历查询是否有解冻过,有解冻的list2
//list2存已解冻的
List<String> policyList2 = new ArrayList<>();
for (String policyno : policyList1) {
//有解冻的返回true
Boolean isfreeze = iClearNoUnfreezeDao.findUnfreezePolicyno(policyno);
if(isfreeze == true){
policyList2.add(policyno);
}
}
//还有异常多条的保单号也去掉 ,不然取保单金额多条时没办法取
List<String> policyList3 =iClearNoUnfreezeDao.findMulti(lastMonth);
// 3)异常的list1-list2-list3
policyList1.removeAll(policyList2);
policyList1.removeAll(policyList3);
for (String policyno : policyList1) {
ApplicationContext ctx = SpringContextHolder.getApplicationContext();
//获取事务
HibernateTransactionManager transactionManager =
(HibernateTransactionManager) ctx.getBean("transactionManager");
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); // 事物隔离级别,开启新事务,这样会比较安全些。
TransactionStatus status = transactionManager.getTransaction(def); // 获得事务状态
try {
n++;
if(n==4){
int s = 1/0;
}
String chdrcoy =policyno.substring(0, 1);
String chdrnum =policyno.substring(1);
//1.调用个险csc
String respinfo = getcscresult(chdrcoy,chdrnum);
//调用解析xml map中{acctmonth=, acctyear=, rootSuccess=true, validflag=1, zappSuccess=true}
Map<String,String> respmap = XmlAnaly.domjxml(respinfo);
//调用者可先判断 response 和 zappsettlement 节点的 Success 属性均为"true",再进行后续解析。
if(respmap.get("rootSuccess").equals("true") && respmap.get("zappSuccess").equals("true")){
System.out.println(respmap.get("validflag"));
if(respmap.get("validflag").equals("2")){//.根据保单佣金结算状态,若佣金已全部发放完毕,则做一次出账处理,清空冻结账户中的余额
String acctmonth =respmap.get("acctmonth");
String acctyear =respmap.get("acctyear");
//如validflag = 2,需要1)wxacctmove中加一条出账流水,参考:bs_conumptionLess
//2)wxacctbaln表中balamt字段减去wxacctbalndetail中的数据中的balamt金额
String openid = freezeLess(policyno);
//3)将wxacctbalndetail中的数据字段accountstatus改为Y和时间
iClearNoUnfreezeDao.updateaccountstatus(policyno); //更新时都更新了????
//4)推送给代理人消息,告知佣金已全额发放
StringBuffer str = new StringBuffer();
str.append("您好,保单(保单号:"+policyno+" 线下佣金中发放,故此保单冻结金额清零。");
String result = getMsessage(openid,str.toString());
}
}
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
}
}
这种写法注意:ApplicationContext ctx = SpringContextHolder.getApplicationContext();
//获取事务
HibernateTransactionManager transactionManager =
(HibernateTransactionManager) ctx.getBean("transactionManager");
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); // 事物隔离级别,开启新事务,这样会比较安全些。
TransactionStatus status = transactionManager.getTransaction(def); // 获得事务状态
这段获取事务一定要放到for循环中,否则再处理第二条数据就会报: do not commit or rollback more than once per transaction,因为放在for循环外面,此时还是一个事务,这点需要注意!!!!!!! 还需要有一下配置:1.applicationContext.xml中配置
<bean id="springContextHolder" class="com.taikang.util.SpringContextHolder"></bean>
需要写个类: import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextHolder.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String beanName) {
return applicationContext.getBean(beanName);
}
public static <T>T getBean(String beanName , Class<T>clazz) {
return applicationContext.getBean(beanName , clazz);
}
}
对于单个数据的事务和这个处理差不多,重要的是for循环中事务的处理会有一些坑!!!!!!!
版权声明:本文为whxwkb原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。