0

I implement a Dashboard functionality that checks every time at program start a list of Requirement-Objects for a bunch of different characteristics like progress, missing data and alike and sets for each characteristic a dedicated beacon on the UI.

protected void initializePerformanceIndicator() {
        try {
            updateA();
            updateB();
            ...
            updateF();
            updateG();
        } catch (Exception e) {
            ErrorHandler.showError("Cannot show KPI Performance", e);
        }
    }

The checks have different compute demands some are faster some slower, therefore each of this checks runs in a dedicated Task to provide some feedback to the user. The skeleton of such a Task is always the same

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();
    }

Before initializePerformanceIndicator is called, I retrieved already elsewhere the data from the database querying a number Spring Repositories:

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;

    }

and as you see updateBudgetKPIController calls getallUserRequirements with the parameter false. Therefore it returns the buffered result set and is not re-fetching data from database. So far everything is fine.

I can run each of these Tasks individually without problem. I tried a number combinations with 2 Tasks. Works fine, but the program will never show more than three or four beacons. Which ones are shown differs as well - what is expected as a consequence of the different Tasks. If I exceed three or four Tasks I often get no error at all, but the UI is just not showing more than three to four beacons.

Sometimes I do get an error message, which is

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. 

I debugged it, and realized that I was generating way too many select statements. The UserRequirement entity has almost a dozen OneToMany relations, some where defined with FetchType.LAZY, so I thought it would be better anyway to configure all these relations as

@OneToMany(fetch = FetchType.LAZY, mappedBy="parent", cascade = CascadeType.ALL) 

Because of the LAZY loading, every Task tries to load additional data in the if(*some criteria*) part.

The problem did not disappear but I get more information, as the error is now

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. 

So I do have a LAZY loading issue.

I am using Spring Boot 2.1.6, MySQL 8.0.15 Community Server, Hibernate Core {5.3.10.Final}, Java 1.8.0_211 and the com.mysql.cj.jdbc.Driver

From a former issue, I have in my properties file the following configuration

# Prevent LazyInitializationException
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

Don't know whether this has a side effect?!

Probably changing the LAZY loading to EAGER will fix it - haven't tried yet - but it would delay program start significantly. Therefore I would prefer a solution with LAZY loading.

Any ideas? I also appreciate any ideas regarding how to further isolate the root cause as the error message is not really explicit and I can't see which part of my code triggers it. Plus when I debug it, the behavior changes as I compute all Tasks sequentially rather then in parallel. Thank you in advance.

Alex
  • 1,387
  • 2
  • 9
  • 14

1 Answers1

0

The issue was caused by different Tasks accessing the same getter of some of the entities. If the first getter call opened a connection, the second call got on it, and then the first call closed the ResultSet, the second call one was in trouble. Synchronizing the getter method solved the problem.

Alex
  • 1,387
  • 2
  • 9
  • 14