java.lang.AbstractMethodError: com.codestudio.sql.PoolManConnectionHandle.prepareStatement(Ljava/lang/String;I)Ljava/sql/PreparedStatement; - spring

I am using Spring 4 and Hibernate 4.3.10 with JPA.
I am implementing poolman connection pooling: below are code of my Hibernate configuration
private static final String PROPERTY_NAME_DATABASE_DRIVER = "jdbc.driverClassName";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "jdbc.password";
private static final String PROPERTY_NAME_DATABASE_URL = "jdbc.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "jdbc.username";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
#Autowired
private Environment environment;
#Bean
public DataSource dataSource() {
DataSource dataSource = null;
try {
dataSource = PoolMan.findDataSource ("oncfo");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class);
entityManagerFactoryBean.setPackagesToScan(environment
.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setJpaProperties(hibProperties());
return entityManagerFactoryBean;
}
private Properties hibProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT,
environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL,
environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
return properties;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
When I am saving data into mysql database getting error like
java.lang.AbstractMethodError: com.codestudio.sql.PoolManConnectionHandle.prepareStatement(Ljava/lang/String;I)Ljava/sql/PreparedStatement;
fulltrace
java.lang.AbstractMethodError: com.codestudio.sql.PoolManConnectionHandle.prepareStatement(Ljava/lang/String;I)Ljava/sql/PreparedStatement;
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$2.doPrepare(StatementPreparerImpl.java:124)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:186)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareStatement(StatementPreparerImpl.java:122)
at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.prepare(IdentityGenerator.java:89)
at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:55)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3032)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3558)
at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:98)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:492)
at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:197)
at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:181)
at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:216)
at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:334)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:289)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:195)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:126)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
at com.sun.proxy.$Proxy49.persist(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:358)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:344)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:329)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy65.save(Unknown Source)
at com.oncfo.serviceImpl.ReadSheetServiceImpl.saveModule(ReadSheetServiceImpl.java:257)
at com.oncfo.serviceImpl.ReadSheetServiceImpl.saveMasterTemplateModularizedV2(ReadSheetServiceImpl.java:87)
at com.oncfo.serviceImpl.ReadSheetServiceImpl.saveExcelSheetComplete(ReadSheetServiceImpl.java:66)

Related

Spring: TransactionRequiredException: No transaction is in progress at entitymanager.flush()

I know there are a lot of posts about this error. But nothing seems working for me. I am getting TransactionRequiredException at entitymanager.flush()
I have config class `
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = “msEntityManagerFactory",
transactionManagerRef = “msTransactionManager",
basePackages = {
x, y, z
})
public class msConfig {
#Bean(name = “msDataSource")
#ConfigurationProperties(prefix = "postgres.datasource.hikari")
public DataSource msDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = “msEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder entityManagerFactoryBuilder,
#Qualifier(“msDataSource") DataSource dataSource) {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
LocalContainerEntityManagerFactoryBean entityManager = entityManagerFactoryBuilder
.dataSource(dataSource)
.packages(“x”,”y”, “z”)
.persistenceUnit("postgres")
.build();
entityManager.setJpaProperties(properties);
return entityManager;
}
#Bean(name = “msTransactionManager")
public PlatformTransactionManager transactionManager(#Qualifier(“msEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
`
And In my service class, I am injecting EntityManager from above class like
public Dummy(#Qualifier(“msEntityManagerFactory") EntityManager entityManager) {
this.entityManager = entityManager;
}
In same class, I have another method that is marked as #Transactional and has entityManager.flush()
What is missing?
Edit:
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:413)
at org.hibernate.internal.SessionImpl.checkTransactionNeededForUpdateOperation(SessionImpl.java:3398)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1355)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1350)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:366)
at com.sun.proxy.$Proxy220.flush(Unknown Source)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:314)
at com.sun.proxy.$Proxy220.flush(Unknown Source)

spring boot and batch throw Invalid object name 'BATCH_JOB_INSTANCE'

I m stucked with spring boot in my app , it's dont want to run schema nor tables , only connect to my database SQL Server and query information, so I disabled this line in file application.properties
spring.batch.initialize-schema=never
spring.datasource.initialization-mode=never
but doesn´t work , I dont know what´s wrong, already configure my datasource, thanks in advance ,
I hope someone can help me, sorry my english is poor.
I am using spring boot 2.2.6, my code of DataSourceConfiguration
.....
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class DataSourceConfiguration {
#Bean
#Primary
#Qualifier("jobsDataSource")
public DataSource hsqldbDataSource() throws SQLException {
final SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriver(new org.hsqldb.jdbcDriver());
dataSource.setUrl("jdbc:hsqldb:mem:mydb");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
#Bean
public JdbcTemplate jdbcTemplate(final DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
#Bean
#Qualifier("sqlserverDataSource")
public DataSource sqlserverDataSource() throws SQLException {
final SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriver(new net.sourceforge.jtds.jdbc.Driver());
dataSource.setUrl("jdbc:jtds:sqlserver://x.y.z.t:1433/mydb");
dataSource.setUsername("usersqlserver");
dataSource.setPassword("xxxxxx");
return dataSource;
}
#Bean
public JdbcTemplate sqlJdbcTemplate(#Qualifier("sqlserverDataSource") final DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
public PlatformTransactionManager transactionManager(){
return new ResourcelessTransactionManager();
}
#Bean
public BatchConfigurer configurer(#Qualifier("sqlserverDataSource") DataSource dataSource){
return new DefaultBatchConfigurer(){
#Override
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager());
return factory.getObject();
// MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean = new MapJobRepositoryFactoryBean(transactionManager());
// mapJobRepositoryFactoryBean.setTransactionManager(transactionManager());
// return mapJobRepositoryFactoryBean.getObject();
}
#Override
protected JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(createJobRepository());
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
};
}
}
Here is the error
Caused by: org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]; nested exception is java.sql.SQLException: Invalid object name 'BATCH_JOB_INSTANCE'.
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:235) ~[spring-jdbc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72) ~[spring-jdbc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443) ~[spring-jdbc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) ~[spring-jdbc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:669) ~[spring-jdbc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:700) ~[spring-jdbc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:712) ~[spring-jdbc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:768) ~[spring-jdbc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.batch.core.repository.dao.JdbcJobInstanceDao.getJobInstance(JdbcJobInstanceDao.java:151) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.repository.support.SimpleJobRepository.isJobInstanceExists(SimpleJobRepository.java:91) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_161]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_161]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at com.sun.proxy.$Proxy54.isJobInstanceExists(Unknown Source) ~[na:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_161]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_161]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at com.sun.proxy.$Proxy46.isJobInstanceExists(Unknown Source) ~[na:na]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.getNextJobParameters(JobLauncherCommandLineRunner.java:199) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.execute(JobLauncherCommandLineRunner.java:191) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.executeLocalJobs(JobLauncherCommandLineRunner.java:166) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.launchJobFromProperties(JobLauncherCommandLineRunner.java:153) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.run(JobLauncherCommandLineRunner.java:148) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:784) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
... 10 common frames omitted
Caused by: java.sql.SQLException: Invalid object name 'BATCH_JOB_INSTANCE'.
at net.sourceforge.jtds.jdbc.SQLDiagnostic.addDiagnostic(SQLDiagnostic.java:372) ~[jtds-1.3.1.jar:1.3.1]
at net.sourceforge.jtds.jdbc.TdsCore.tdsErrorToken(TdsCore.java:2988) ~[jtds-1.3.1.jar:1.3.1]
at net.sourceforge.jtds.jdbc.TdsCore.nextToken(TdsCore.java:2421) ~[jtds-1.3.1.jar:1.3.1]
Thanks in advance
Spring Batch requires a few tables in your database to run. So you need to create those tables manually upfront or tell Spring Boot to do it for you.
Since you told Spring Boot to not create those tables for you by setting spring.batch.initialize-schema=never, then you need to create them yourself in your target datasource.
So in your case, you need to run the DDL script to create Spring Batch tables on your SQL server instance before running your job.
Alright,I have had removing this line and I did as you suggested and it´s worked, only that dao is NullpointerException, it´s interface and is used in the Itemprocessor:
#Bean
public BatchConfigurer configurer(#Qualifier("sqlserverDataSource") DataSource dataSource){
return new DefaultBatchConfigurer(){
// #Override
// protected JobRepository createJobRepository() throws Exception {
// JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
// factory.setDataSource(dataSource);
// factory.setTransactionManager(transactionManager());
// // factory.setIsolationLevelForCreate("ISOLATION_READ_COMMITTED");
// //factory.setTablePrefix("dbo.tmpAccWebServ");
// //factory.setDatabaseType("SQLSERVER");
// //factory.setMaxVarCharLength(1000);
// return factory.getObject();
//// MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean = new MapJobRepositoryFactoryBean(transactionManager());
//// mapJobRepositoryFactoryBean.setTransactionManager(transactionManager());
//// return mapJobRepositoryFactoryBean.getObject();
// }
//
// #Override
// protected JobLauncher createJobLauncher() throws Exception {
// SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
// jobLauncher.setJobRepository(createJobRepository());
// jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
// jobLauncher.afterPropertiesSet();
// return jobLauncher;
// }
#Override
public void setDataSource(DataSource dataSource) {
// override to do not set datasource even if a datasource exist.
// initialize will use a Map based JobRepository (instead of database)
} //
};
}//
This is the code of CustomerItemProcessor
public class CustomerItemProcessor implements ItemProcessor<beangenerico,ThreadLocal<CopyOnWriteArrayList<beanCustomer>>> {
#Autowired
private CustomerDAO customerDAO;
private ThreadLocal<CopyOnWriteArrayList<beanCustomer>> listbean = new ThreadLocal<CopyOnWriteArrayList<beanCustomer>>();
public ThreadLocal<CopyOnWriteArrayList<beanCustomer>> process(beangenerico rangos) throws Exception {
System.out.println("entro a customitemprocessor");
listbean.set(new CopyOnWriteArrayList<beanCustomer>());
System.out.println("rangos:"+rangos.getIni()+"-"+rangos.getFin());
listbean = customerDAO.getAccAgentes(rangos);
if(listbean != null) {
//customer.setId(currentCustomer.getId());
return listbean;
} else {
return null;
}
}
#Autowired
public void setCustomerDAO(CustomerDAOImpl customerDAO) {
this.customerDAO = customerDAO;
}
}
This is the code of interface
public interface CustomerDAO {
ThreadLocal<CopyOnWriteArrayList<beanCustomer>> getAccAgentes(beangenerico bean);
}
This is the code if CustomerDAOImpl
#Repository
#Component
public class CustomerDAOImpl implements CustomerDAO{
private String SP_SQL = "{call mysp(?, ?)}";
#Autowired
#Qualifier("sqlserverDataSource")
DataSource dataSource;
#Autowired
JdbcTemplate jdbcTemplate;
public ThreadLocal<CopyOnWriteArrayList<beanCustomer>> getAccAgentes(beangenerico bean) {
...
//query resulset
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
}
Thanks in advance for your help.

LocalDate deserialization &serialization error -jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of java.time.LocalDate

The birthdate of the employee stored in the Elastic seems to be stored as an incorrect datatype. I have setup code for Elasticseach client with object mapper and registered JavaTime module. I am trying to pull employees list from Elasticsearch and it doesn't seem to work and is failing at deserializing.
Jar Versions:
org.elasticsearch.client:elasticsearch-rest-client -> 6.4.3 (*)
org.elasticsearch.client:elasticsearch-rest-high-level-client -> 6.4.3
org.elasticsearch:elasticsearch -> 6.4.3
org.springframework.data:spring-data-elasticsearch -> 3.1.4.RELEASE
com.fasterxml.jackson.datatype:jackson-datatype-hppc -> 2.9.8
com.fasterxml.jackson.core:jackson-core:2.9.8
com.fasterxml.jackson.core:jackson-databind:2.9.8 (*)
com.fasterxml.jackson.datatype:jackson-datatype-jsr310 -> 2.9.8 (*)
com.fasterxml.jackson.datatype:jackson-datatype-hibernate5 -> 2.9.8
com.fasterxml.jackson.core:jackson-annotations -> 2.9.0
com.fasterxml.jackson.module:jackson-module-afterburner -> 2.9.8
com.fasterxml.jackson.module:jackson-module-parameter-names:2.9.8 (*)
com.fasterxml.jackson.module:jackson-modules-java8:2.9.8
Build.Gradle:
compile "com.fasterxml.jackson.datatype:jackson-datatype-hppc"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
compile "com.fasterxml.jackson.datatype:jackson-datatype-hibernate5"
compile "com.fasterxml.jackson.core:jackson-annotations"
compile "com.fasterxml.jackson.core:jackson-databind"
compile "com.fasterxml.jackson.module:jackson-module-afterburner"
runtime group: 'com.fasterxml.jackson.module', name: 'jackson-modules-java8', version: '2.9.8', ext: 'pom'
// https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-parameter-names
compile group: 'com.fasterxml.jackson.module', name: 'jackson-module-parameter-names', version: '2.9.8'
compile 'org.elasticsearch:elasticsearch'
compile 'org.elasticsearch.client:transport'
compile 'org.elasticsearch.client:elasticsearch-rest-client'
compile 'org.elasticsearch.client:elasticsearch-rest-high-level-client'
compile 'org.springframework.data:spring-data-elasticsearch'
application.yml
jackson:
serialization:
WRITE_DATES_AS_TIMESTAMPS: false
jpa:
open-in-view: false
ElasticConfiguration Code:
#AutoConfigureAfter(value = { JacksonConfiguration.class })
public class ElasticsearchConfiguration {
RestHighLevelClient client = null;
#Bean
public RestHighLevelClient buildClient() {
return new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http"),
new HttpHost("localhost", 9201, "http")));
}
#Bean
#Primary
public ElasticsearchOperations elasticsearchTemplate(final ElasticsearchConverter elasticsearchConverter,
EntityMapper mapper, Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder ) {
return new ElasticsearchTemplate((Client) buildClient(), new CustomEntityMapper(new ObjectMapper()));
}
//jackson2ObjectMapperBuilder.createXmlMapper(false).build()
public class CustomEntityMapper implements EntityMapper {
private ObjectMapper objectMapper;
public CustomEntityMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//objectMapper.registerModule(new JavaTimeModule());
JavaTimeModule javaTimeModule=new JavaTimeModule();
// Hack time module to allow 'Z' at the end of string (i.e. javascript json's)
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE_TIME));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_DATE));
objectMapper.registerModule(javaTimeModule);
objectMapper.registerModule(new ParameterNamesModule());
objectMapper.registerModule(new Jdk8Module());
objectMapper.findAndRegisterModules();
}
#Override
public String mapToString(Object object) throws IOException {
return objectMapper.writeValueAsString(object);
}
#Override
public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
return objectMapper.readValue(source, clazz);
}
}
}
Error:
Caused by: org.springframework.data.elasticsearch.ElasticsearchException: failed to map source [ {"id":73,"name":"K****","birthDate":{"year":2019,"month":"MAY","dayOfYear":150,"leapYear":false,"dayOfMonth":30,"dayOfWeek":"THURSDAY","era":"CE","monthValue":5,"chronology":{"id":"ISO","calendarType":"iso8601"}},"role..}] to class Employee
at org.springframework.data.elasticsearch.core.AbstractResultMapper.mapEntity(AbstractResultMapper.java:45) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
at org.springframework.data.elasticsearch.core.DefaultResultMapper.mapResults(DefaultResultMapper.java:102) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
at org.springframework.data.elasticsearch.core.ElasticsearchTemplate.queryForPage(ElasticsearchTemplate.java:315) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
at org.springframework.data.elasticsearch.core.ElasticsearchTemplate.queryForPage(ElasticsearchTemplate.java:309) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
at org.springframework.data.elasticsearch.core.ElasticsearchTemplate.queryForPage(ElasticsearchTemplate.java:139) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
at org.springframework.data.elasticsearch.repository.support.AbstractElasticsearchRepository.findAll(AbstractElasticsearchRepository.java:126) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
at org.springframework.data.elasticsearch.repository.support.AbstractElasticsearchRepository.findAll(AbstractElasticsearchRepository.java:120) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_211]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_211]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_211]
at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_211]
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.time.LocalDate` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"id":73,"name":"Kelly","birthDate":{"year":2019,"month":"MAY","dayOfYear":150,"leapYear":false,"dayOfMonth":30,"dayOfWeek":"THURSDAY","era":"CE","monthValue":5,"chronology":{"id":"ISO","calendarType":"iso8601"}},"role...}},"lastModifiedBy""[truncated 396 chars]; line: 1, column: 38] (through reference chain: Employee["birthDate"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1452) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1028) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1297) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004) ~[jackson-databind-2.9.8.jar:2.9.8]
at org.springframework.data.elasticsearch.core.DefaultEntityMapper.mapToObject(DefaultEntityMapper.java:82) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
at org.springframework.data.elasticsearch.core.AbstractResultMapper.mapEntity(AbstractResultMapper.java:43) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
... 34 common frames omitted
Employee:
public class Employee extends AbstractAuditingEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
//Remaining Fields are removed for brevity
AbstractAuditEntity
#MappedSuperclass
#Audited
#EntityListeners({AuditingEntityListener.class, EntityAuditEventListener.class})
public abstract class AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
#CreatedBy
#Column(name = "created_by", nullable = true, length = 50, updatable = false)
private String createdBy;
#CreatedDate
#Column(name = "created_date")
private LocalDateTime createdDate = LocalDateTime.now();
#LastModifiedBy
#Column(name = "last_modified_by", length = 50)
private String lastModifiedBy;
#LastModifiedDate
#Column(name = "last_modified_date")
private LocalDateTime lastModifiedDate = LocalDateTime.now();
Your problem probably will be because Jackson does not support Java 8 DateTime Api out of the box.
So you can add the following library to add support for that (add in pom.xml):
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.4.0</version>
</dependency>
and configure object mapper to use this library module like :
ObjectMapper mapper = new ObjectMapper()
.registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule());
Use the link for reference :
https://github.com/FasterXML/jackson-modules-java8
I have successfully managed to use java.time types in Spring Data Elasticsearch with the following configuration:
#Configuration
#EnableElasticsearchRepositories(basePackages = "org.example.search.repository")
public class ElasticsearchConfig {
#Bean
public ElasticsearchTemplate elasticsearchTemplate(Client client) {
return new ElasticsearchTemplate(client, new CustomEntityMapper());
}
static class CustomEntityMapper implements EntityMapper {
private final ObjectMapper mapper;
CustomEntityMapper() {
mapper = new ObjectMapper()
.setDefaultPropertyInclusion(Include.NON_NULL)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.findAndRegisterModules();
}
#Override
public String mapToString(Object object) throws IOException {
return mapper.writeValueAsString(object);
}
#Override
public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
return mapper.readValue(source, clazz);
}
}
}
It is more of a workaround but by adding the annotation mentioned below over the respective localdate/localdatetime fields.
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
#JsonSerialize(using = LocalDateTimeSerializer.class)
For those who are looking for a solution on version ES-8 and Spring Boot:3.0
Create a configuration class extending ElasticsearchConfiguration and override clientConfiguration and elasticsearchClient creation.
During elasticsearchClient creation inject your own objectMapper configured to use Java 8 time module, which will override the default objectMapper.
#Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder()
.connectedTo(<Hostname> +":"+ <Port>)
.usingSsl()
.withBasicAuth(<Username>, <Password>)
.build();
}
#Override
public ElasticsearchClient elasticsearchClient(RestClient restClient) {
Assert.notNull(restClient, "restClient must not be null");
//Create Java8 time module
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DateFormat.date_time_no_millis.getPattern())));
//Register the module with objectMapper
ObjectMapper objectMapper=new ObjectMapper()
.registerModule(module);
//To convert datetime to ISO-8601
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
//Creating our own jsonpMapper
JsonpMapper jsonpMapper=new JacksonJsonpMapper(objectMapper);
// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(
restClient, jsonpMapper);
// And create the API client
return new ElasticsearchClient(transport);
}
Maven dependency:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.14.0</version>
</dependency>

Spring Batch job throwing StackOverflowError: null error when started

I have a simple controller that takes a JSON string of file paths and runs a spring batch job on these files. To implement spring batch I am following a tutorial that would eventually produce the code in https://github.com/michaelhoffmantech/patient-batch-loader.
#RestController
public class JobController {
private static final Log _logger = LogFactory.getLog(JobController.class);
#Autowired
private JobLauncher jobLauncher;
#Autowired
private Job job;
#RequestMapping(value = "/job/import", method = RequestMethod.POST)
public void importAsset(#RequestBody String uploadedFiles) throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
_logger.debug("importAsset() >>");
try{
_logger.debug("Uploaded files: ");
_logger.debug(uploadedFiles);
_logger.debug("Putting values into the parameter map...");
Map<String, JobParameter> parameterMap = new LinkedHashMap<>();
parameterMap.put(Constants.JOB_PARAM_UPLOADED_FILES, new JobParameter(uploadedFiles));
_logger.debug("Launching job [" + job.getName() + "]...");
jobLauncher.run(job, new JobParameters(parameterMap));
_logger.debug("<< importAsset()");
}
catch (Exception e){
String errorMessage = "An error occured while importing a batch. " + e.getLocalizedMessage();
_logger.error(errorMessage, e);
throw e;
}
}
}
Batch configuration is:
#Configuration
#EnableBatchProcessing
public class BatchConfig implements BatchConfigurer {
#Autowired
private PlatformTransactionManager platformTransactionManager;
#Autowired
private DataSource dataSource;
private JobRepository jobRepository;
private JobExplorer jobExplorer;
private JobLauncher jobLauncher;
#Override
public JobRepository getJobRepository() throws Exception {
return this.jobRepository;
}
#Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return this.platformTransactionManager;
}
#Override
public JobLauncher getJobLauncher() throws Exception {
return this.jobLauncher;
}
#Override
public JobExplorer getJobExplorer() throws Exception {
return this.jobExplorer;
}
protected JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(this.jobRepository);
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(this.dataSource);
factory.setTransactionManager(getTransactionManager());
factory.afterPropertiesSet();
return factory.getObject();
}
#PostConstruct
public void afterPropertiesSet() throws Exception{
this.jobRepository = createJobRepository();
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
jobExplorerFactoryBean.setDataSource(this.dataSource);
jobExplorerFactoryBean.afterPropertiesSet();
this.jobExplorer = jobExplorerFactoryBean.getObject();
this.jobLauncher = createJobLauncher();
}
}
And finally the job configuration is:
#Configuration
public class BatchJobConfig {
private static final Log _logger = LogFactory.getLog(BatchJobConfig.class);
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Bean
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry){
JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
postProcessor.setJobRegistry(jobRegistry);
return postProcessor;
}
#Bean
public Step step() throws Exception{
return this.stepBuilderFactory
.get(Constants.STEP_NAME)
.tasklet(new Tasklet() {
#Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
_logger.debug("execute() >>");
_logger.debug("<< execute()");
return RepeatStatus.FINISHED;
}
})
.build();
}
#Bean
public Job job(Step step){
return this.jobBuilderFactory
.get(Constants.JOB_NAME)
.validator(validator())
.start(step)
.build();
}
public JobParametersValidator validator(){
return new JobParametersValidator(){
#Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
_logger.debug("validate() >>");
String filePathsJsonStr = parameters.getString(Constants.JOB_PARAM_UPLOADED_FILES);
if(StringUtils.isBlank(filePathsJsonStr)){
throw new JobParametersInvalidException("'" + Constants.JOB_PARAM_UPLOADED_FILES + "' parameter is required for job '" + Constants.JOB_NAME + "'.");
}
Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<UploadFile>>(){}.getType();
ArrayList<UploadFile> uploadedFiles = gson.fromJson(filePathsJsonStr, listType);
for(UploadFile uploadFile: uploadedFiles){
File file = new File(uploadFile.getPath());
if(!file.exists()){
throw new JobParametersInvalidException("File '" + uploadFile.getPath() + "' did not exist or was not readable.");
}
}
_logger.debug("<< validate()");
}
};
}
}
When I run the batch job through the REST method. I get the following in the log:
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController : importAsset() >>
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController : Uploaded files:
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController : [{"path":"C:\\app-import-staging\\cat-pet-animal-domestic-104827.jpeg","username":"test"},{"path":"C:\\app-import-staging\\kittens-cat-cat-puppy-rush-45170.jpeg","username":"test"}]
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController : Putting values into the parameter map...
2018-09-29 20:00:51.681 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController : Launching job [app-import]...
2018-09-29 20:00:51.745 ERROR 10104 --- [nio-8102-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause
java.lang.StackOverflowError: null
at java.util.HashMap.putVal(HashMap.java:657) ~[na:1.8.0_181]
at java.util.HashMap.put(HashMap.java:612) ~[na:1.8.0_181]
at java.util.HashSet.add(HashSet.java:220) ~[na:1.8.0_181]
at java.util.Collections$SynchronizedCollection.add(Collections.java:2035) ~[na:1.8.0_181]
at java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:508) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at com.sun.proxy.$Proxy83.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor93.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]
What I noticed was that the part:
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at com.sun.proxy.$Proxy83.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor93.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]
goes on and on until it throws a StackOverflowError.
Any advice or help on what to change to fix this would be appreciated.
Change in BatchConfig.java
#Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return this.platformTransactionManager;
}
To
#Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return new DataSourceTransactionManager(dataSource);
}
I ended up simplifying the job by removing BatchJobConfig and modifying the BatchConfig class like below:
#Configuration
#EnableBatchProcessing
public class BatchConfig {
private static final Log _logger = LogFactory.getLog(BatchConfig.class);
#Bean
public Job job(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory){
Step step = stepBuilderFactory.get(Constants.STEP_NAME)
.tasklet(new Tasklet() {
#Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
_logger.debug("execute() >>");
_logger.debug("<< execute()");
return RepeatStatus.FINISHED;
}
})
.build();
return jobBuilderFactory.get(Constants.JOB_NAME)
.incrementer(new RunIdIncrementer())
.validator(validator())
.start(step)
.build();
}
public JobParametersValidator validator(){
return new JobParametersValidator(){
#Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
_logger.debug("validate() >>");
String filePathsJsonStr = parameters.getString(Constants.JOB_PARAM_UPLOADED_FILES);
if(StringUtils.isBlank(filePathsJsonStr)){
throw new JobParametersInvalidException("'" + Constants.JOB_PARAM_UPLOADED_FILES + "' parameter is required for job '" + Constants.JOB_NAME + "'.");
}
Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<UploadFile>>(){}.getType();
ArrayList<UploadFile> uploadedFiles = gson.fromJson(filePathsJsonStr, listType);
for(UploadFile uploadFile: uploadedFiles){
File file = new File(uploadFile.getPath());
if(!file.exists()){
throw new JobParametersInvalidException("File '" + uploadFile.getPath() + "' did not exist or was not readable.");
}
}
_logger.debug("<< validate()");
}
};
}
}
And after this, the job executed as expected.

Autowired JpaRepository Save Ignored in custom FieldSetMapper

In brief, I am trying to use JpaRepositories in spring batch related classes. But due to some reason, only selects are working but Insert/updates are ignored.
I have a custom FieldSetMapper as below,
#Component
#StepScope // I have not seen step scope is used as this way!
public class ItemFieldSetMapper implements FieldSetMapper<CategoryItem> {
private CSVSchema schema;
private Task task;
Logger logger = Logger.getLogger(LocalPersist.class.getName());
#Autowired
private CSVColumnDao csvColumnDao;
#Autowired
private BasisCategoryDao basisCategoryDao;
#Override
public BasisCategoryItem mapFieldSet(FieldSet fieldSet) throws BindException {
// csvColumnDao.save(someobject) // This is ignored
}
}
Since I have to start the job asynchronously (Job is started form controller method)
I created a custom DefaultBatchConfigurer as below,
#Component
#EnableBatchProcessing
public class ProductImportBatchConfig extends DefaultBatchConfigurer {
#Autowired
private TaskExecutor taskExecutor; // This is coming form another bean defined in another configuration class.
/*#Autowired
private DataSource dataSource;*/
#Autowired
private PlatformTransactionManager transactionManager;
#Override
protected JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(super.getJobRepository());
jobLauncher.setTaskExecutor(taskExecutor);
return jobLauncher;
}
#Override
public PlatformTransactionManager getTransactionManager() {
// TODO Auto-generated method stub
return transactionManager;
}
#Override
#Autowired
public void setDataSource(DataSource dataSource) {
DatabasePopulatorUtils.execute(dropDatabasePopulator(), dataSource);
DatabasePopulatorUtils.execute(createDatabasePopulator(), dataSource);
// TODO Auto-generated method stub
super.setDataSource(dataSource);
}
private DatabasePopulator dropDatabasePopulator() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
databasePopulator.setContinueOnError(true);
databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-drop-mysql.sql"));
return databasePopulator;
}
private DatabasePopulator createDatabasePopulator() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
databasePopulator.setContinueOnError(true);
databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-mysql.sql"));
return databasePopulator;
}
}
This is my Application class,
#Configuration
#ComponentScan
#EnableAutoConfiguration(exclude = { BatchAutoConfiguration.class })
public class Application extends SpringBootServletInitializer {
private static Class<Application> applicationClass = Application.class;
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
#Override
protected final SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
#Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
pool.setCorePoolSize(6);
pool.setMaxPoolSize(30);
pool.setWaitForTasksToCompleteOnShutdown(false);
return pool;
}
}
What I have figured so far:
I went through many documentation to get my scenario working. This is the closest I got.
The issue is to do with #EnableBatchProcessing. It creates its own DataSourceTransactionManager which has no clue about jpa/hibernate and ignore inserts/updates.
As the suggested solution,
#Bean
public BatchConfigurer batchConfigurer(DataSource dataSource, EntityManagerFactory entityManagerFactory) {
return new BasicBatchConfigurer(dataSource, entityManagerFactory);
}
This seems to be not working because the constructor changed in newer versions.
It seems like, I have to get rid of the #EnableBatchProcessing and get the same transaction manager to work both in application and batch contexts.
Here are some trial and errors,
If I keep the #EnableBatchProcessing and use #EnableAutoConfiguration (exclude = { BatchAutoConfiguration.class })
The Batch tables are created and application runs correctly, But during the run time I get the following.
java.lang.StackOverflowError: null
at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936) ~[na:1.8.0_72]
at org.springframework.aop.framework.AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport.java:487) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:193) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at com.sun.proxy.$Proxy126.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor110.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_72]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_72]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at com.sun.proxy.$Proxy126.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor110.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_72]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_72]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at com.sun.proxy.$Proxy126.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor110.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_72]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_72]
Here is the properties file I am using,
spring.datasource.url = jdbc:mysql://localhost:3306/db
spring.datasource.username =root
spring.datasource.password =root
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.validationQuery = SELECT 1
spring.datasource.timeBetweenEvictionRunsMillis = 3600
spring.datasource.minEvictableIdleTimeMillis = 3600
spring.datasource.testWhileIdle = true
spring.jpa.database = MYSQL
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto =update
spring.jpa.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming_strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.batch.job.enabled=false
spring.view.prefix=/WEB-INF/jsp/
spring.view.suffix=.jsp
But when I comment #EnableBatchProcessing the application complains for
JobBuilderFactory bean and etc. It seems like use of same transaction manager is not a practice and I have not see auto wiring of Jpa Repositories in batch related classes (Eg, ItemWriters and Readers etc.). But I want this scenario to work since this is a migration from a different implementation to spring batch.
I was confused with the injecting jpa transaction manager into the spring batch. This is how I was able to do it.
#Configuration
#EnableBatchProcessing
public class ProductImportBatchConfig implements BatchConfigurer {
private static final Log logger = LogFactory.getLog(DefaultBatchConfigurer.class);
#Autowired
private TaskExecutor taskExecutor;
#Autowired
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
#Autowired
private LocalContainerEntityManagerFactoryBean entityManagerFactory;
private JobRepository jobRepository;
private JobLauncher jobLauncher;
private JobExplorer jobExplorer;
protected ProductImportBatchConfig() {
}
#Override
public JobRepository getJobRepository() {
return jobRepository;
}
#Override
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
#Override
public JobLauncher getJobLauncher() {
return jobLauncher;
}
#Override
public JobExplorer getJobExplorer() {
return jobExplorer;
}
#Autowired
public void setDataSource(DataSource dataSource) {
if (this.dataSource == null) {
logger.error(null, new Throwable("This is not acceptable"));
}
DatabasePopulatorUtils.execute(dropDatabasePopulator(), this.dataSource);
DatabasePopulatorUtils.execute(createDatabasePopulator(), this.dataSource);
this.dataSource = dataSource;
EntityManagerFactory object = entityManagerFactory.getObject();
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(object);
this.transactionManager = jpaTransactionManager;
}
#PostConstruct
public void initialize() {
try {
if (dataSource == null) {
logger.warn("No datasource was provided...using a Map based JobRepository");
if (this.transactionManager == null) {
this.transactionManager = new ResourcelessTransactionManager();
}
MapJobRepositoryFactoryBean jobRepositoryFactory = new MapJobRepositoryFactoryBean(
this.transactionManager);
jobRepositoryFactory.afterPropertiesSet();
this.jobRepository = jobRepositoryFactory.getObject();
MapJobExplorerFactoryBean jobExplorerFactory = new MapJobExplorerFactoryBean(jobRepositoryFactory);
jobExplorerFactory.afterPropertiesSet();
this.jobExplorer = jobExplorerFactory.getObject();
} else {
this.jobRepository = createJobRepository();
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
jobExplorerFactoryBean.setDataSource(this.dataSource);
jobExplorerFactoryBean.afterPropertiesSet();
this.jobExplorer = jobExplorerFactoryBean.getObject();
}
this.jobLauncher = createJobLauncher();
} catch (Exception e) {
throw new BatchConfigurationException(e);
}
}
public JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(taskExecutor);
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
public JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.afterPropertiesSet();
return factory.getObject();
}
// Batch related scripts
private DatabasePopulator dropDatabasePopulator() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
databasePopulator.setContinueOnError(true);
databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-drop-mysql.sql"));
return databasePopulator;
}
private DatabasePopulator createDatabasePopulator() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
databasePopulator.setContinueOnError(true);
databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-mysql.sql"));
return databasePopulator;
}
}

Resources