Java的循環遍歷,如何保證每個循環體都是單獨的事務

        不知道大家在實際開發的過程中,有沒有遇到過類似的情況:比如有這麼一個定時任務,需要定時處理一些超時未付款的訂單。然後你查詢所有符合條件的記錄(假設有10條記錄),接着for循環遍歷每一個訂單,每個循環體都要執行大量的業務,比如商品庫存回退,訂單狀態修改,業務流水添加,消息推送等。你前五個循環都執行成功了,可是執行第六個循環的時候突然拋出一個異常,導致你前面五個執行成功的訂單都事務回滾了,就有種三國的赤壁之戰,曹軍鐵索連舟,一榮俱榮,一損俱損的感覺,是不是很蛋疼?你還真別笑,我接手公司項目的時候,還真的就看到前面的開發這樣寫代碼。沒辦法,既然現在是咱接手,還就得老老實實的填坑了。
       解決方案其實很簡單,只需要一兩段代碼就能解決,本人親測有效,當時測試的方式是:查詢的10條記錄,設置第6條出現異常,然後在10個循環都結束完後,發現只有第6條記錄保持原樣(異常時事務回滾),而它前面的5條和後面的4條都正常執行,並沒有受影響。

我的項目用的是ssm框架,使用spring自帶的定時任務,下面開始上僞代碼:

先是OrderTimer.java類:該類是獲取所有符合要求的記錄,也就是獲取這個集合,然後開始遍歷執行每條符合要求的記錄,注意具體執行的業務要封裝到另一個方法中,也就是封裝到業務層處理,不單單只是因爲代碼規範,更主要的是因爲封裝在另一個方法,才能更隨心所欲的設置事務的傳播屬性。

package demo;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class OrderTimer {

    private static Logger logger = Logger.getLogger(OrderTimer.class);

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderMapper orderMapper;

    /**
     * 每隔1分鐘執行未付款訂單的關閉的操作
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    public void closeOrders() {
        logger.info("--------開始每隔1分鐘執行未付款訂單的關閉的操作");

        //獲取所有超時未付款的訂單,這個需要根據你自己的實際情況編寫,此處只是舉例
        List<Order> list= orderMapper.listCloseOrder();
        if(list==null||list.size()<1){
            return ;
        }

        for (Order order : list) {
            orderService.closeOrder(order);
        }
    }

}

 

下面這個是實現層的代碼,重點留意兩個“關注點”:

package demo;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

@Service
public class OrderServiceImpl implements OrderService {

    private static Logger logger=Logger.getLogger(OrderServiceImpl.class);

    @Autowired
    private OrderMapper orderMapper;

		/**
		這個註解很重要,意思是設置當前方法的事務傳播級別爲REQUIRES_NEW,表示當前方法內的所有事務都是獨立的,不影響整體的事務。
		有的項目使用註解的方式配置當前方法傳播屬性會無效,此時可能需要你去你的spring-mybatis.xml文件中配置,效果是一樣的
		*/
    @Transactional(propagation = Propagation.REQUIRES_NEW)//關注點一!!!
    @Override
    public void closeOrder(Order order) {

        try{

		//這裏,執行你自己的業務,比如商品庫存回退,訂單狀態改變,操作流水等。
		//重點關注的是@Transactional註解和TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
				

            
        }catch (Exception e){
            logger.info("網絡異常:"+e.getMessage());
            
            //這一段表示手動回滾事務,此處的try-catch,是表示當前方法如果出錯了,
            //那我就自己消化這個異常,不再往外拋,處理異常的方式是手動回滾事務。
            //如此,每個循環體都自己處理自己的事務,不管成功與失敗,都不影響整個循環
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//關注點二!!!
        }

    }

 
}

 

       大致的邏輯便是如此,這種東西無法分享具體的代碼,因爲涉及到事務就得有數據庫操作,有數據庫操作就得建庫建表,不可能全部寫出來,那樣不管對寫的人還是看的人都很難受,所以不如省略具體的核心業務,只關注幾個需要關注的點,然後以你現有的項目爲基礎實現出來並親自測試一下。當然,既然是僞代碼,就需要有一定代碼量的人才能腦補啊。

       至此,分享結束,希望能幫到諸位!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章