Java事務失效


問題復現,用偽代碼復現問題!

事務配置文件

<tx:advice id="txAdvice" transaction-manager="transactionManager">   
    <tx:attributes>
        <!-- TODO 事務隔離級別可以嘗試 NESTED -->      
        <tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>      
        <tx:method name="create*" propagation="REQUIRED" rollback-for="Exception"/>
        <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
        <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
        <tx:method name="*" read-only="true" rollback-for="Exception"/>
      </tx:attributes>
 </tx:advice>

controller層代碼

/** * excel批量導入信息 */
@RequestMapping(path="/pre/pretemplate/importExcel/{templateType}")
public Map<String, Object> importCrm(@PathVariable String templateType, MultipartFile[] file, HttpServletRequest req) throws Exception{   
    User user = getSessionUser(req);
    Map<String, Object> data = null;
		//0 老師 1 學生
		if("0".equals(templateType)) {
			data = service.updateExcel(file[0], req,templateType);
			if ((boolean) data.get("success")) {    
				service.writeSystemLog("批量導入老師數據成功");
			} else {
				service.writeSystemLog("批量導入老師數據失敗");
			}
		}
    
    	if("1".equals(templateType)) {
			data = service.updateExcel(file[0], req,templateType);
			if ((boolean) data.get("success")) {    
				service.writeSystemLog("批量導入學生數據成功");
			} else {
				service.writeSystemLog("批量導入學生數據失敗");
			}
		}
    return data;
}

service層代碼

public Map<String, Object> updateExcel(MultipartFile file, HttpServletRequest req, String templateType) throws Exception {
    .........//權限與數據的處理
     data = importService.importLevelExcel(file, templateType);
    if (!(boolean) data.get("success")) {
			return data;
		}
		// 將數據插入數據庫
		data.put("msg", "信息導入成功!");
		return data;
}

 public Map<String, Object> importLevelExcel(MultipartFile file, String templateType) throws Exception {
     HSSFWorkbook hwb;
        Map<String, Object> map = new LinkedHashMap<>();
        hwb = new HSSFWorkbook(file.getInputStream());
        HSSFSheet sheet = hwb.getSheetAt(0);
     try{
     		........
    		String target = getCellValue(row.getCell(2));
            String method = getCellValue(row.getCell(3));
         //檢驗是否包含特殊字符
         checkForString(method);
     }catch (Exception e) {
				map.put("success", false);
				map.put("msg", "數據類型異常,請檢查!");
				return map;
			}
 }

業務需求:需要在用Excel模板導入信息時進行判斷,Windows系統下新建文件夾不支持的符號不能導入!

因為導入模板信息較多,所以我寫了一個公共方法,在導入時進行調用進行校驗

public void checkForString(String str) throws Exception {    
    String regEx = "[\\/:*?\"<>|]";    
    Pattern p = Pattern.compile(regEx);    
    Matcher m = p.matcher(str);    
    bolean b = m.find();    
    if (b) {        
        throw new BusinessException("數據中不能包含:“\\\\/:*?\"<>|”中任何字符!");    
    }
}

本以為這樣就結束了,沒想到經過測試后發現,消息提示什么的都沒問題,但是即便是在模板信息中含有特殊字符的情況下,還是可以成功保存幾條數據,也就是說事務有問題,順着思路去配置文件一看,只有service里面的第一級方法有事務,所以在配置文件中添加了導入方法的事務。

<tx:method name="importLevelExcel" propagation="REQUIRES_NEW" rollback-for="Exception"/>

本想着加完就可以愉快的結束,沒想到問題還是沒有解決,於是我上百度上遨游了一圈,發現當前加事務的方法中異常被try-catch捕獲了,導致異常處理器不能捕捉到異常,事務出現問題。

但是方法中不僅要返回彈框提示信息,還有返回日志信息,保存用戶操作日志,這該怎么辦?

經過思考我決定將異常放在service最頂級方法處理,根據異常類進行信息回寫,service代碼改成

public Map<String, Object> updateExcel(MultipartFile file, HttpServletRequest req, String templateType) throws Exception {
    .........//權限與數據的處理
        try{
     data = importService.importLevelExcel(file, templateType);
    } catch (BusinessException bus) {
            data.put("success", false);
            data.put("msg", "導入模板中不能包含:“\\/:*?\"<>|”中任何字符!");
            return data;
        } catch (Exception e) {
            data.put("success", false);
            data.put("msg", "數據類型異常,請檢查!");
            return data;
        }
    return data;
}

 public Map<String, Object> importLevelExcel(MultipartFile file, String templateType) throws Exception {
     HSSFWorkbook hwb;
        Map<String, Object> map = new LinkedHashMap<>();
        hwb = new HSSFWorkbook(file.getInputStream());
        HSSFSheet sheet = hwb.getSheetAt(0);
     
     		........
    		String target = getCellValue(row.getCell(2));
            String method = getCellValue(row.getCell(3));
         //檢驗是否包含特殊字符
         checkForString(method);
    
 }

到此為止,我覺得我已經大功告成,但是經過測試,還有問題,我就有點納悶了!

經過查詢才知道,原來同類中子方法的事務是無效的,什么嵌套還有傳播通通無效,但是我想想自己的父級方法不是有事務嗎?為什么還是失效,最后才明白,原來是我在父級方法中try-catch了,導致整體都沒有事務了

解決辦法有兩種:

1.將子方法放在其他service下

2.調用子方法前,重新獲取bean對象

於是我在service中重新獲取bean對象,就OK了

總結:

1.spring事務管理中,如果方法參與了事務,就不能在方法中進行try-catch,否則事務控制器無法捕獲事務,事務失效

2.同類方法中,如果父類參與了事務管理,那么即便子類重新添加了事務,哪怕是不同傳播等級的事務,還是無效,spring默認集成父類的事務。如果想子類單獨處理事務,有兩種解決方案

A:將子方法放在其他service下

B:調用子方法前,重新獲取bean對象

如若有誤,歡迎指正!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM