Spring Boot + MySQL:LAZY加载问题-语句关闭后不允许执行任何操作

时间:2019-07-20 10:08:51

标签: mysql spring-boot spring-data-jpa lazy-loading resultset

我实现了仪表板功能,该功能每次在程序启动时都会检查需求对象列表,以查看一堆不同的特性,例如进度,数据丢失等,并为每个特性在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加载的解决方案。

有什么想法吗?我也很欣赏关于如何进一步隔离根本原因的任何想法,因为错误消息并不是很明确,而且我看不到代码的哪一部分触发了它。另外,当我调试它时,行为会发生变化,因为我依次而不是并行地计算所有任务。预先谢谢你。

1 个答案:

答案 0 :(得分:0)

此问题是由不同的任务访问某些实体的同一吸气剂引起的。如果第一个getter调用打开了一个连接,第二个调用接通了它,然后第一个调用关闭了ResultSet,则第二个调用遇到了麻烦。同步getter方法解决了这个问题。