Spring Boot+ MySQL:延迟加载问题 - 语句关闭后不允许进行任何操作
Spring Boot+ MySQL: LAZY Loading issue - No operations allowed after statement closed
我实现了一个仪表板功能,每次在程序启动时检查需求对象列表中的一系列不同特征,如进度、丢失数据等,并为每个特征设置一个专用信标 UI.
protected void initializePerformanceIndicator() {
try {
updateA();
updateB();
...
updateF();
updateG();
} catch (Exception e) {
ErrorHandler.showError("Cannot show KPI Performance", e);
}
}
这些检查有不同的计算需求,有些更快,有些更慢,因此每个检查 运行 都在一个专门的任务中为用户提供一些反馈。这样一个任务的骨架总是一样的
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。因此它 returns 缓冲的结果集并且不从数据库中重新获取数据。目前一切正常。
我可以 运行 这些任务中的每一个都没有问题。我尝试了 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.
我调试了它,发现我生成了太多 select 语句。 UserRequirement 实体有近十几个 OneToMany 关系,其中一些用 FetchType.LAZY 定义,所以我认为无论如何将所有这些关系配置为
会更好
@OneToMany(fetch = FetchType.LAZY, mappedBy="parent", cascade = CascadeType.ALL)
由于延迟加载,每个任务都会尝试在 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.
所以我确实遇到了延迟加载问题。
我正在使用 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 加载的解决方案。
有什么想法吗?我也很欣赏关于如何进一步隔离根本原因的任何想法,因为错误消息并不是很明确,而且我看不到我的代码的哪一部分触发了它。另外,当我调试它时,行为会随着我按顺序而不是并行计算所有任务而发生变化。提前谢谢你。
问题是由不同的任务访问某些实体的相同 getter 引起的。如果第一个 getter 调用打开了一个连接,第二个调用接通了它,然后第一个调用关闭了 ResultSet,那么第二个调用就有麻烦了。同步 getter 方法解决了问题。
我实现了一个仪表板功能,每次在程序启动时检查需求对象列表中的一系列不同特征,如进度、丢失数据等,并为每个特征设置一个专用信标 UI.
protected void initializePerformanceIndicator() {
try {
updateA();
updateB();
...
updateF();
updateG();
} catch (Exception e) {
ErrorHandler.showError("Cannot show KPI Performance", e);
}
}
这些检查有不同的计算需求,有些更快,有些更慢,因此每个检查 运行 都在一个专门的任务中为用户提供一些反馈。这样一个任务的骨架总是一样的
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。因此它 returns 缓冲的结果集并且不从数据库中重新获取数据。目前一切正常。
我可以 运行 这些任务中的每一个都没有问题。我尝试了 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.
我调试了它,发现我生成了太多 select 语句。 UserRequirement 实体有近十几个 OneToMany 关系,其中一些用 FetchType.LAZY 定义,所以我认为无论如何将所有这些关系配置为
会更好@OneToMany(fetch = FetchType.LAZY, mappedBy="parent", cascade = CascadeType.ALL)
由于延迟加载,每个任务都会尝试在 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.
所以我确实遇到了延迟加载问题。
我正在使用 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 加载的解决方案。
有什么想法吗?我也很欣赏关于如何进一步隔离根本原因的任何想法,因为错误消息并不是很明确,而且我看不到我的代码的哪一部分触发了它。另外,当我调试它时,行为会随着我按顺序而不是并行计算所有任务而发生变化。提前谢谢你。
问题是由不同的任务访问某些实体的相同 getter 引起的。如果第一个 getter 调用打开了一个连接,第二个调用接通了它,然后第一个调用关闭了 ResultSet,那么第二个调用就有麻烦了。同步 getter 方法解决了问题。