當資料來自另一個(假定已關閉)事務時,我在訪問正在運行的事務中的資料時遇到問題。我有三個如下所示的類,其中一個物體(稱為 MyEntity)還有另一個通過 Hibernate 映射連接的物體,稱為“OtherEntity”,它的延遲加載設定為 true。注意我有兩個交易:
- 一個加載物體串列
- 每個新專案的新交易
但是,即使我在方法中有一個活動事務(TransactionSynchronizationManager.isActualTransactionActive 為真),這也會在“無會話”的回圈內失敗。
我真的不明白這個問題。在我看來,即使第一個事務應該完成,第二個事務使用的物件“屬于”第一個事務?也許它是一個競爭條件?
@Service
class ServiceA {
@Autowired
private ServiceB serviceB;
@Autowired
private ServiceC serviceC;
public void test() {
List<MyEntity> allEntities = serviceC.loadAllEntities(); //First transaction ran, getting a list of entities, but due to lazy loading we havent loaded all the data
for(MyEntity i : allEntities) {
serviceB.doOnEach(i); //On each element a new transaction should start
}
}
}
@Service
class ServiceB {
@Transactional
public void doOnEach(MyEntity entity) {
System.out.println(TransactionSynchronizationManager.isActualTransactionActive()); //true, therefore we have an active transaction here
OtherEntity other = entity.getSomeOtherEntity(); //Want to load the "lazy loaded" entity here
//"No Session" exception is thrown here
}
}
@Service
class ServiceC {
@Autowired
private MyRepository myRepository;
@Transactional
public List<MyEntity> loadAllEntities() {
return myRepository.findAll();
}
}
一個解決方案是在“doOnEach”方法中重新加載“MyEntity”實體,但在我看來,這似乎是一個次優的解決方案,尤其是在大串列中。為什么我要重新加載所有應該存在的資料?
任何幫助表示贊賞。
顯然,真正的代碼比這要復雜得多,但出于業務原因,我必須有這些單獨的事務,所以請不要使用重寫核心邏輯的“解決方案”。我只是想了解這里發生了什么。
uj5u.com熱心網友回復:
在呼叫loadAllEntities()完成后,Spring 代理提交事務并關閉關聯的 Hibernate Session。這意味著你不能讓 Hibernate 透明地加載未加載的惰性關聯,而無需明確告訴它這樣做。
如果出于某種原因您確實希望延遲加載關聯的物體,則您擁有的兩個選項要么Hibernate.initialize(entity.getSomeOtherEntity())在您的doOnEach()方法中使用,要么將spring.jpa.open-in-view屬性設定為 true 以讓OpenSessionInViewInterceptor為您執行此操作。
JOIN FETCH否則,最好通過存盤庫查詢或物體圖將它們與父物體一起加載。
參考:
- https://www.baeldung.com/spring-open-session-in-view
- https://www.baeldung.com/hibernate-initialize-proxy-exception
進一步澄清:
Spring 在進入loadAllEntities()方法之前創建一個事務并打開一個新的 Session (A),并在回傳時提交/關閉它們。當您呼叫entity.getSomeOtherEntity()已加載的原始會話 (A) 時entity(即已entity分離),而是在進入doOnEach()事務方法時創建了一個新會話 (B)。顯然 Session (B) 對它的關系一無所知entity,同時someOtherEntity內部的 Hibernate 代理entity參考了原始 Session (A) 并且對 Session (B) 一無所知。要使 Hibernate 代理someOtherEntity實際使用當前活動的 Session (B),您可以呼叫Hibernate.initialize().
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/477142.html
