I have an autowired jpa repository object working. However, I need to use it to add rows into the database from multiple threads.
Though, after passing it to another thread, it fails.
Code structure
@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(); } }
However, doing a dbRepository.save() from a thread safe class, I get error
cause: java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext@41330d4f has been closed already
detailedMessage: 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
Stacktrace:
{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)"
How can I use the spring boot repository object across multiple threads ?
Advertisement
Answer
The problem is that your run()
method just schedules the tasks to be executed, but does not wait for their completion. This what is happening:
new SpringApplicationBuilder(Application.class)
You are creating a new application context with the command line runnerApplication
.run(args)
Then you initialize and execute your application context’srun()
method- The
run()
method schedules the tasks to be executed and exists immediately:
public void run(final String... args) { for (FileStatus fileStatus: fileStatuses) { executors.execute(new ThreadSafeClass(dbRepository)); } }
Because
run()
terminated, spring assumes that the application has finished and calls.close();
Thus closing the application context and making it impossible to use any spring features such as repositories.The scheduled tasks get executed, but the context was already closed, thus they fail and throw the exception.
The solution is to wait for the tasks’ completion before exiting from the run method. As your example is too minimal, this is just an example. Alternatively you can use other methods to wait for the completion of the tasks such as CountDownLatch , etc, without having to shutdown the thread pool:
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