Репозиторий JPA Spring boot, переданный в другой поток, не работает

#java #multithreading #spring-boot #spring-data-jpa #executor

Вопрос:

У меня работает объект репозитория jpa с автоматической проводкой. Однако мне нужно использовать его для добавления строк в базу данных из нескольких потоков.
Хотя, после передачи его другому потоку, он терпит неудачу. Структура кода

 @SpringBootApplication(exclude = HealthcheckConfig.class)
public class Application implements CommandLineRunner {

    @Autowired
    private DBRepository dbRepository;

    @Autowired
    private AppConfig appConfig;

    private ExecutorService executors = Executors.newFixedThreadPool(3);

    Application() {
    }

    @Override
    public void run(final String... args) {

        final DBSchemaObject temp = new Application("testdb", "testfield");
        dbRepository.save(temp); // This WORKs!!!
       
        for (FileStatus fileStatus: fileStatuses) {
            executors.execute(new ThreadSafeClass(dbRepository));
        }
    }

    public static void main(final String[] args) {
        new SpringApplicationBuilder(Application.class)
                .web(WebApplicationType.NONE)
                .run(args)
                .close();
    }
}

 

Однако, делая DbRepository.save() из потокобезопасного класса, я получаю
причину ошибки: java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext@41330d4f has been closed already

Подробный отчет: Error creating bean with name 'spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties': Could not bind properties to 'DataSourceProperties' : prefix=spring.datasource, ignoreInvalidFields=false, ignoreUnknownFields=true

Трассировка стека:

 {StackTraceElement@14839} "org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)"
{StackTraceElement@14840} "org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)"
{StackTraceElement@14841} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354)"
{StackTraceElement@14842} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)"
{StackTraceElement@14843} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)"
{StackTraceElement@14844} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)"
{StackTraceElement@14845} "org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)"
{StackTraceElement@14846} "org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)"
{StackTraceElement@14847} "org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)"
{StackTraceElement@14848} "org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)"
{StackTraceElement@14849} "org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:410)"
{StackTraceElement@14850} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1334)"
{StackTraceElement@14851} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)"
{StackTraceElement@14852} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)"
{StackTraceElement@14853} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)"
{StackTraceElement@14854} "org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)"
{StackTraceElement@14855} "org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)"
{StackTraceElement@14856} "org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)"
{StackTraceElement@14857} "org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)"
{StackTraceElement@14858} "org.springframework.beans.factory.support.DefaultListableBeanFactory$1.orderedStream(DefaultListableBeanFactory.java:481)"
{StackTraceElement@14859} "org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.detectPersistenceExceptionTranslators(PersistenceExceptionTranslationInterceptor.java:167)"
{StackTraceElement@14860} "org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:149)"
{StackTraceElement@14861} "org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)"
{StackTraceElement@14862} "org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174)"
{StackTraceElement@14863} "org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)"
{StackTraceElement@14864} "org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)"
{StackTraceElement@14865} "org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)"
{StackTraceElement@14866} "org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)"
{StackTraceElement@14867} "com.sun.proxy.$Proxy99.save(Unknown Source)"
{StackTraceElement@14868} "com.xxxx.run(Application.java:109)"
{StackTraceElement@14869} "java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)"
{StackTraceElement@14870} "java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)"
{StackTraceElement@14871} "java.lang.Thread.run(Thread.java:748)"
 

Как я могу использовать объект репозитория spring boot в нескольких потоках ?

Комментарии:

1. Не могли бы вы, пожалуйста, опубликовать всю трассировку стека?

2. Добавлена трассировка стека!

3. Ну, run() метод вашего приложения завершается, когда вы создаете задачи, что закрывает контекст. Вам следует дождаться завершения созданных задач, прежде чем возвращаться из Application::run()

Ответ №1:

Проблема в том, что ваш run() метод просто планирует выполнение задач, но не ждет их завершения. Вот что происходит:

  1. new SpringApplicationBuilder(Application.class) Вы создаете новый контекст приложения с помощью программы командной строки Application
  2. .run(args) Затем вы инициализируете и выполняете метод контекста вашего приложения run()
  3. run() Метод планирует выполнение задач и существует немедленно:
  public void run(final String... args) {
        for (FileStatus fileStatus: fileStatuses) {
            executors.execute(new ThreadSafeClass(dbRepository));
        }
    }
 
  1. Поскольку run() работа завершена, spring предполагает, что приложение завершено, и вызывает .close(); , таким образом, закрывая контекст приложения и делая невозможным использование каких-либо функций spring, таких как репозитории.
  2. Запланированные задачи выполняются, но контекст уже был закрыт, поэтому они терпят неудачу и создают исключение.

Решение состоит в том, чтобы дождаться завершения задач перед выходом из метода run. Поскольку ваш пример слишком минимален, это всего лишь пример. В качестве альтернативы вы можете использовать другие методы для ожидания завершения задач , таких как обратный отсчет и т.д., Без необходимости завершать работу пула потоков:

 for (FileStatus fileStatus: fileStatuses) {
    executors.execute(new ThreadSafeClass(dbRepository));
}
executors.shutdown(); // prevents the executor from accepting any new tasks
executors.awaitTermination(); // wait for the tasks to finish
 

ExecutorService::завершение работы javadoc

ExecutorService::Ожидание завершения javadoc

Комментарии:

1. Ааа, хорошо! Спасибо за улов!!