1

我使用spring-boot 1.5.4和hibernate-core 5.2.10。在事務結束時彈簧引導不需要的SQL UPDATE

我有一個控制器,它調用一個服務方法(可以將其命名爲pService)保存一個實體的一些邏輯之後

pService.save是這樣的:

@Transactional(readOnly = false, rollbackFor = Exception.class, propagation = Propagation.REQUIRED) 
public <S extends SomeEntity> S save(S entity) throws ServiceException { 
    //some logic 

    entity.setAttrX("original text"); 
    S justSavedEntity = someEntityRepository.save(entity); // POINT 1 

    //we termporarily change one attribute just to get a legible representation calling another service method (lets name it eService) 
    eService.process(justSavedEntity); 

    return justSavedEntity; //POINT 2 

} 

eService.process是什麼像這樣:

@Transactional(readOnly = false, propagation = Propagation.MANDATORY) 
public void process(SomeEntity entity) { 

    //some logic 
    entity.setAttrX("someText"); 
    //method ends here 
} 

因爲沒有其他呼叫分開一個保存點1,我期待文本「原文」被保存在數據庫中,但文我得到保存是在第2點更改的文本。

那麼,爲什麼我要保存「someText」而不是「原始文本」?

我可以在數據庫日誌中看到,在POINT 1之後運行SQL INSERT命令並且pService返回時,以及不需要的SQL UPDATE命令運行時,將「原始文本」更改爲導致錯誤的「someText」。

爲什麼這個「多餘的」UPDATE命令發出?

在此先感謝!

回答

2

這是JPA規範描述的預期行爲。您更改了一個受管理的實體,該實體在邏輯上與更改存儲的數據的目的相同,因此在事務完成之前,實體的狀態將與數據庫同步。這個想法是,你不必在方法結束時調用保存 - 這是多餘的。

阿諾德Galovics寫了pretty good blog post about this與漂亮的圖表。

鑑於此,顯然你不應該用這種方式來改變實體。它表明你所調用的'流程'不是將它作爲一個實體傳遞給它的對象。這是一個常見的錯誤,認爲僅僅因爲你當前的對象似乎在不同的上下文中提供了你需要的所有東西,所以在該上下文中使用它是一個好主意,而不是檢查對象是否代表相同的概念。在這種情況下,您的流程不需要實體。

您應該創建某種類型的值對象以傳遞給過程方法。創建一個值對象可以像將實體從它的持久性上下文中分離一樣簡單:Spring JpaRepository - Detach and Attach entity

或者,您可以計算該字段的值,並將其作爲第二個參數傳遞給eService上的過程方法。您的最終選擇是按實際情況發送實體,並讓接收服務計算'可讀'表示。

但是,您還有其他問題。如果您轉換到spring-data-jpa而不是編寫自己的存儲庫類,您可能會意識到它們是什麼。簡而言之,存儲庫類旨在存儲和檢索某種持久存儲中的實體,但不打算調用其他服務。首先,這將是任何代碼使用回購的責任。在spring-mvc應用程序中,這通常是一個控制器,用於編排應用程序服務。

說白了,你不應該從回購類中調用服務。除了實體上的CRUD之外,它什麼也不做,絕對沒有別的。如果你需要它來做別的事情,那麼你可能做了其他錯誤的事情,或者你選擇了錯誤的應用程序架構。

你應該看看spring-data-jpa。它的默認配置是在運行時自動生成回購的實現。這可能會減少您的代碼庫,爲您節省大量時間和缺陷,並且不會輕易讓您犯這類架構錯誤。而且,最重要的是,它比現在做的要容易得多。

+0

非常感謝您的回答。這很奇怪,因爲我們只有在升級到spring-boot 1.5,1.1和1.2後纔會看到這種行爲需要一個repository.save調用來保存一個實體。任何想法改變了? – Dreal

+0

對於你所看到的有太多可能的解釋,所以對你的問題的任何迴應都是純粹的推測。但是你應該知道,這個行爲是核心JPA規範的一部分,並且一直以這種方式在Spring的jpa子系統中實現。 –

+0

另外,我編輯了答案,在最後添加了相關內容。 –