在使用Spring的事務註解@Transactional的時候遇到幾個坑
今天在用@Transactional的時候遇到幾個很奇怪的問題,一段從舊程式上拷過來的程式碼結果死活不執行,讓我百思不得其解。
舊的程式碼是這樣的,一直執行正常
新的程式碼是這樣的,結果儲存操作不起作用@Override public Pager getPager(Map<String, Object> paramMap) { List<TaSjjhLog> logList = TaSjjhLog.getWaitingList(); if(logList != null && logList.size() > 0){ this.saveList(logList); } return this.dao.getPager(paramMap); } @Transactional private void saveList(List<TaSjjhLog> logList) { for (TaSjjhLog log : logList) { log.setErrorMessage(this.handleErrorMsg(log.getErrorMessage())); this.dao.save(log); } TaSjjhLog.removeAllFromWaitingList(logList); }
@Override public List<ChangeLog> getList(Pager<ChangeLog> pager, String mc) { List<changelog> logList = ChangeLog.getWaitingList(); if(logList != null && logList.size() > 0){ this.service.saveList(logList); } return this.dao.getList(pager, mc); } @Transactional private void saveList(List<ChangeLog> logList) { logList.stream().forEach(log->{ log.setErrorMessage(this.handleErrorMsg(log.getErrorMessage(), log.getRecordCount(), log.getExecutedCount())); this.dao.save(log); ChangeLog.removeFromWaitingList(log); }); }</changelog>
起初我還以為是Lambda表示式的問題,改寫了一下,結果是一樣的。
然後我就不停的測試,不停的改@transactional的事務級別,結果都沒用。
然後我就看我的dao裡面的基類,結果發現了一點不同
舊程式碼在dao的基類上加了@Transactional,而新程式碼則是加了@Transactional(readOnly=true)
所以dao裡面的save方法,舊程式碼是有事務的,而新程式碼則是隻讀的事務。可以問題來了,為什麼我在新程式碼的saveList上加的事務註解不起作用?
我在網上沒有找到相應的解釋,但是通過我的測試和對spring的認識,我覺得spring的事務是通過切面(或者也可以說是代理)實現的,當你呼叫一個代理類的介面,如果其標註了事務註解,則會生效,
而我這個saveList是在service裡面自己的方法呼叫的,這個註解不會被處理。
於是我修改程式碼,結果使用了三種方案全部失敗:
1. 在getList上加事務註解,取消saveList上的註解:這種方式可以儲存資料成功,但是查詢出來的資料在SpringMVC轉換結果為json的時候報錯,說no session.
2. 在dao基類的save方法上加註解:結果同上
3. service提供兩個介面給controller,分別是getList和saveList,都加上事務註解:結果還是同上。
經驗告訴我,當你的請求包含了提交事務的時候,你同時又讀了資料,並且需要在controller或是view層懶載入資料,則會有問題,因為事務一提交就關閉了。
我唯一不能理解的是我先呼叫saveList,提交了事務,儲存了資料,我再呼叫getList,按理應該重新生成事務,可是為什麼到controller這裡還是沒有session了。
最後沒辦法,我把讀寫兩個事情在controller層也分開為兩個介面,在jsp裡面做兩次請求來處理。
總結一下:
1. service裡面的方法如果不是對外的介面,加事務註解沒有用
2. 一個請求裡面如果包含了提交事務(非只讀),同時又查詢資料的話,查詢的資料要立即載入,不能到view層再呼叫session去懶載入資料。