我实现了仪表板功能,该功能每次在程序启动时都会检查需求对象列表,以查看一堆不同的特性,例如进度,数据丢失等,并为每个特性在UI上设置专用信标。
protected void initializePerformanceIndicator() {
try {
updateA();
updateB();
...
updateF();
updateG();
} catch (Exception e) {
ErrorHandler.showError("Cannot show KPI Performance", e);
}
}
这些检查具有不同的计算需求,有些检查速度更快,有些则更慢,因此,每种检查都在专用的Task中运行,以向用户提供一些反馈。此类任务的框架始终相同
protected void updateA() throws Exception {
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
embeddedBudgetKPIController.setHyperlink("Budget", null);
embeddedBudgetKPIController.setToolTip("...");
ObservableList<UserRequirement> issues = FXCollections.observableArrayList();
List<UserRequirement> requirements = reqService.getAllUserRequirements(false); // all requirements of the selected product
for(UserRequirement req: requirements) {
if(*some criteria*) {
issues.add(req);
}
}
if(issues.isEmpty()) {
embeddedBudgetKPIController.setBeaconColor(Color.GREEN);
} else {
embeddedBudgetKPIController.setBeaconColor(Color.RED);
}
return null;
};
};
task.setOnSucceeded(e -> {
// Nothing to do
});
Thread tt = new Thread(task);
tt.start();
}
在调用initializePerformanceIndicator之前,我已经在其他地方从数据库中检索了一些Spring存储库中的数据:
protected final ObservableList<UserRequirement> allUserRequirements = FXCollections.observableArrayList();
public synchronized ObservableList<UserRequirement> getAllUserRequirements(boolean forceUpdate) throws Exception {
logger.debug(""); // show that this method is called
Product selectedProduct = SelectedScope.getSelectedProduct();
if(selectedProduct == null) {
throw new Exception("No selProduct selected");
}
if(forceUpdate || allUserRequirements.isEmpty()) {
allUserRequirements.clear();
allUserRequirements.addAll(epicRepository.findByProductAndRevisionSuccessorIsNull(selectedProduct));
allUserRequirements.addAll(themeRepository.findByProductAndRevisionSuccessorIsNull(selectedProduct));
allUserRequirements.addAll(userStoryRepository.findByProductAndRevisionSuccessorIsNull(selectedProduct));
allUserRequirements.addAll(tangibleRepository.findByProductAndRevisionSuccessorIsNull(selectedProduct));
}
return allUserRequirements;
}
,您将看到updateBudgetKPIController使用参数 false 调用getallUserRequirements。因此,它返回缓冲的结果集,而不是从数据库中重新获取数据。到目前为止,一切都很好。
我可以单独运行这些任务,而不会出现问题。我尝试了2个任务的数字组合。工作正常,但该程序永远不会显示超过三个或四个信标。所显示的内容也有所不同-由于不同任务的预期结果。如果我超过三个或四个任务,我通常不会出现任何错误,但是UI不会显示超过三到四个信标。
有时我会收到一条错误消息,即
WARN 08:14 o.h.e.j.s.SqlExceptionHelper.logExceptions:137: SQL Error: 0, SQLState: S1009
ERROR 08:14 o.h.e.j.s.SqlExceptionHelper.logExceptions:142: No operations allowed after statement closed.
我调试了它,并意识到我生成了太多选择语句。 UserRequirement实体几乎有十二个OneToMany关系,其中一些是使用FetchType.LAZY定义的,所以我认为将所有这些关系配置为
@OneToMany(fetch = FetchType.LAZY, mappedBy="parent", cascade = CascadeType.ALL)
由于LAZY加载,每个任务都尝试在if(*some criteria*)
部分中加载其他数据。
问题并没有消失,但是我得到了更多的信息,因为现在是错误
WARN 11:02 o.h.c.i.AbstractPersistentCollection.withTemporarySessionIfNeeded:278: Unable to close temporary session used to load lazy collection associated to no session
WARN 11:02 o.h.e.j.s.SqlExceptionHelper.logExceptions:137: SQL Error: 0, SQLState: S1009
ERROR 11:02 o.h.e.j.s.SqlExceptionHelper.logExceptions:142: No operations allowed after statement closed.
所以我确实有LAZY加载问题。
我正在使用Spring Boot 2.1.6,MySQL 8.0.15 Community Server,Hibernate Core {5.3.10.Final},Java 1.8.0_211和com.mysql.cj.jdbc.Driver
从先前的问题来看,我的属性文件中具有以下配置
# Prevent LazyInitializationException
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
不知道这是否有副作用?!
可能将LAZY加载更改为EAGER可以修复-尚未尝试-但这会大大延迟程序启动。因此,我希望使用LAZY加载的解决方案。
有什么想法吗?我也很欣赏关于如何进一步隔离根本原因的任何想法,因为错误消息并不是很明确,而且我看不到代码的哪一部分触发了它。另外,当我调试它时,行为会发生变化,因为我依次而不是并行地计算所有任务。预先谢谢你。
答案 0 :(得分:0)
此问题是由不同的任务访问某些实体的同一吸气剂引起的。如果第一个getter调用打开了一个连接,第二个调用接通了它,然后第一个调用关闭了ResultSet,则第二个调用遇到了麻烦。同步getter方法解决了这个问题。