1. 程式人生 > >手動提交事務和在for迴圈裡進行事務控制及宣告式事務的使用例項!

手動提交事務和在for迴圈裡進行事務控制及宣告式事務的使用例項!

事務場景:需要做一個跑批進行一個流程的實現,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迴圈中事務的處理會有一些坑!!!!!!!