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 方法解决了问题。