Проблема с повторным использованием EntityManager, когда соответствующая угроза прервана - PullRequest
0 голосов
/ 10 марта 2020

Я пытался решить эту проблему сам и гуглить решение онлайн, но я все еще не понимаю, как это исправить. Пожалуйста, помогите!

На моем веб-сайте Spring Framework у меня есть очень трудоемкая служба (GroupSearchServiceImpl), которая запрашивает базу данных MySQL и сохраняет ее результаты в HttpSession. Я хочу иметь возможность отменить эту услугу и запустить ее снова с новыми параметрами. Иногда работает, но время от времени выдает java.lang.IllegalStateException: Session/EntityManager is closed. Смотрите вывод журнала в конце этого поста.

Вот подробности (я пропустил некоторый код):

Контроллер:

@Controller
public class GroupSearchController {

    private Future<Void> asyncResult;

    @RequestMapping(value = "/submission/{submissionId:\\d+}/group_search/", method = 
                    RequestMethod.POST)
    public ModelAndView groupSearch(@PathVariable("submissionId") final long submissionId, final 
                                    HttpSession session, final Model model, @Valid final 
                                    FilterForm form, final Errors errors) {

        if (errors.hasErrors()) {
            return new ModelAndView("submission/group_search");
        }

        Submission submission = submissionService.findSubmission(submissionId);

        // Cancel previous task if running
        if (asyncResult != null) {
            asyncResult.cancel(true);
        }

        // Run group search
        asyncResult = groupSearchService.groupSearch(submission, session, form.getSpecies(), form.getSource(), form.getDisease(), new AtomicBoolean(true));

        return new ModelAndView("submission/group_search");
    }
}

Служба:

@Service
public class GroupSearchServiceImpl implements GroupSearchService {
    @Override
    @Async
    @Transactional(propagation = Propagation.REQUIRED)
    public Future<Void> groupSearch(Submission submission, HttpSession session, String species, String source, String disease,
                            AtomicBoolean running) {

        LOGGER.info(String.format("Group search is started on thread %s (species: %s, source: %s, disease: %s)",
                Thread.currentThread().getName(),
                species != null ? species : "all",
                source != null ? source : "all",
                disease != null ? disease : "all"));

        for (File file : submission.getFiles()) {

                if (Thread.currentThread().isInterrupted()) break;

                List<Spectrum> querySpectra = file.getSpectra();
                for (Spectrum querySpectrum : querySpectra) {

                    if (Thread.currentThread().isInterrupted()) break;

                    List<SpectrumClusterView> clusters = MappingUtils.toList(
                            spectrumRepository.searchConsensusSpectra(
                                    querySpectrum, 0.25, 0.01, species, source, disease));
            }
        }

        if (Thread.currentThread().isInterrupted())
            LOGGER.info(String.format("Group search is cancelled on thread %s (species: %s, source: %s, disease: %s)",
                    Thread.currentThread().getName(),
                    species != null ? species : "all",
                    source != null ? source : "all",
                    disease != null ? disease : "all"));

        return new AsyncResult<>(null);
    }
}

Репозиторий:

public class SpectrumRepositoryImpl implements SpectrumRepositoryCustom {
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public Iterable<SpectrumClusterView> searchConsensusSpectra(
            Spectrum querySpectrum, double scoreThreshold, double mzTolerance,
            String species, String source, String disease) {

        List<SpectrumClusterView> resultList  = 
            entityManager.createNativeQuery("SOME SQL QUERY", 
                SpectrumClusterView.class).getResultList();

        return resultList;
    }
}

Конфигурация:

@Configuration
@EnableAsync
@EnableTransactionManagement(mode = AdviceMode.PROXY, proxyTargetClass = false)
@EnableJpaRepositories(basePackages = "org.dulab.adapcompounddb.site.repositories",
        entityManagerFactoryRef = "entityManagerFactoryBean", transactionManagerRef = "jpaTransactionManager")
@ComponentScan(basePackages = {"org.dulab.adapcompounddb.site", "org.dulab.adapcompounddb.rest"},
        excludeFilters = @ComponentScan.Filter({Controller.class, ControllerAdvice.class}))
@Import({WebSecurityConfiguration.class})
public class ApplicationContextConfiguration {

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
        final HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");

        final LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(adapter);
        factory.setDataSource(dataSource);
        factory.setPackagesToScan("org.dulab.adapcompounddb.models.entities");
        factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
        factory.setValidationMode(ValidationMode.NONE);

        final Map<String, Object> jpaPropertyMap = new HashMap<>();
        jpaPropertyMap.put("javax.persistence.schema-generation.database.action", "none");
        jpaPropertyMap.put("hibernate.order_by.default_null_ordering", "last");
        jpaPropertyMap.put("hibernate.enable_lazy_load_no_trans", true);
        factory.setJpaPropertyMap(jpaPropertyMap);

        return factory;
    }
}

И, наконец, вывод журнала:

14:45:55.863 [SimpleAsyncTaskExecutor-1]   INFO  org.dulab.adapcompounddb.site.services.GroupSearchServiceImpl: Group search is started on thread SimpleAsyncTaskExecutor-1 (species: all, source: all, disease: all)
14:46:33.920 [SimpleAsyncTaskExecutor-2]   INFO  org.dulab.adapcompounddb.site.services.GroupSearchServiceImpl: Group search is started on thread SimpleAsyncTaskExecutor-2 (species: human, source: all, disease: all)
14:46:49.948 [SimpleAsyncTaskExecutor-1]   INFO  org.dulab.adapcompounddb.site.services.GroupSearchServiceImpl: Group search is cancelled on thread SimpleAsyncTaskExecutor-1 (species: all, source: all, disease: all)
14:47:07.991 [SimpleAsyncTaskExecutor-3]   INFO  org.dulab.adapcompounddb.site.services.GroupSearchServiceImpl: Group search is started on thread SimpleAsyncTaskExecutor-3 (species: human, source: ileum, disease: all)
14:47:12.903 [SimpleAsyncTaskExecutor-2]   INFO  org.dulab.adapcompounddb.site.services.GroupSearchServiceImpl: Group search is cancelled on thread SimpleAsyncTaskExecutor-2 (species: human, source: all, disease: all)
14:47:31.822 [SimpleAsyncTaskExecutor-4]   INFO  org.dulab.adapcompounddb.site.services.GroupSearchServiceImpl: Group search is started on thread SimpleAsyncTaskExecutor-4 (species: all, source: ileum, disease: all)
14:47:31.871 [SimpleAsyncTaskExecutor-4]   ERROR org.dulab.adapcompounddb.site.services.GroupSearchServiceImpl: Error during the group search on thread SimpleAsyncTaskExecutor-4 (species: all, source: ileum, disease: all): Session/EntityManager is closed
java.lang.IllegalStateException: Session/EntityManager is closed

Итак, когда я отмена SimpleAsyncTaskExecutor-3, затем SimpleAsyncTaskExecutor-4 выдает исключение java.lang.IllegalStateException: Session/EntityManager is closed, я не понимаю, почему и как это исправить.

ОБНОВЛЕНИЕ: я установил максимальный размер пула исполнителя потока для 1, и это, кажется, помогло.

    @Bean
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        return executor;
    }
...