在我的 Spring Boot(版本 2.5.4)中,我有一個服務通過ExecutorService(在新執行緒中)執行其方法之一。在這個新執行緒中,我訪問了我的一些存盤庫(JpaRepositorys)。我在JpaRepository'sgetById()和 中看到了不同的行為findById(),我進行了搜索但沒有找到。
這是我的物體:
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class LimoSet {
@Id
@Column(name = "_id")
private String id;
@Column(name = "_status")
private String status;
@OneToMany(mappedBy = "set", fetch = FetchType.EAGER)
private Set<Limo> limos = new LinkedHashSet<>();
@Column(name = "_statistics")
private String statistics;
}
這是存盤庫:
@Repository
public interface LimoSetRepository extends JpaRepository<LimoSet, String> {
}
這是服務:
@Service
@Transactional
public class GeneratorService {
private final static Logger logger = LoggerFactory.getLogger(GeneratorService.class);
private final LimoSetRepository setRepository;
private final ExecutorService executor = Executors.newFixedThreadPool(3);;
public void generate(Options opts) {
.
.
.
Callable<String> task = () -> {
try {
this.runGenerate(opts);
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
var set = setRepository.getById(opts.getName());
set.setStatus(e.getMessage());
setRepository.save(set);
}
return "ok";
};
executor.submit(task);
}
void runGenerate(Options opts) throws JsonProcessingException {
.
.
.
var set = setRepository.findById(opts.getName()).get(); //this is ok
var set = setRepository.getById(opts.getName()); //this throws LazyInitializationException
set.setStatus("GENERATED"); //the Exception is reported in this line
setRepository.save(set);
}
}
為什么findById()有效但getById()無效?
uj5u.com熱心網友回復:
有幾個因素可以總結為 LazyInitializationException。
對于引物:
結識的不同的語意findById和getById
findById獲取整個物件getById獲取物件代理
請參閱:使用 JPA 和 Hibernate 時 find 和 getReference EntityManager 方法如何作業
加載代理時需要注意,
LazyInitializationException如果在 EntityManager 關閉后嘗試訪問代理參考,則可能會拋出a 。
其次,您需要了解為什么runGenerate在沒有活動事務的情況下運行,即使類被標記為@Transactional。(注意:沒有活動事務是關閉EntityManager的根本原因)
要確保事務未處于活動狀態,請參閱檢測 Spring 事務是否處于活動狀態:
assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
為什么不傳播來自測驗的父事務
runGenerate通過執行程式在單獨的執行緒上運行- 活動事務作為執行緒本地保存,因此事務不會跨執行緒傳播。
為什么@Transactional對runGenerate沒有影響?
Spring(默認情況下)使用 CGLIB 代理來啟用方面。CGLIB 代理有兩個要求:
- 代理方法必須是公共的
- 您必須通過代理(而不是通過實際物件)呼叫該方法
這些條件都不滿足。
為什么交易在測驗本身中是活躍的
Spring 提供了一個特殊的TestExecutionListener呼叫TransactionalTestExecutionListener,它檢查測驗方法是否被標記為@Transactional,并在需要時啟動事務
怎么修
- 提取
runGenerate到服務,將其標記為公共和@Transactional - 將新服務自動連接到您的測驗中
- 更一般地說,請注意您的任務的哪些部分在事務背景關系下運行(您的任務的捕獲塊不是事務性的)
- 更一般地說,了解臟檢查。一旦您的方法在事務背景關系下運行,
save呼叫將是多余的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/363267.html
