1. 程式人生 > >在使用Spring的事務註解@Transactional的時候遇到幾個坑

在使用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去懶載入資料。