How can I use java configuration of spring batch without spring boot? - java

I'm new to spring batch and I'm trying to learn about it.
I implemented datasource and batch configuration but it doesn't seem to work, because
Exception in thread "main" 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.SQLSyntaxErrorException: Table '$tableName.batch_job_instance' doesn't exist
DataSourceConfiguration class :
#Configuration
public class DataSourceConfiguration {
#Bean
public DataSource mysqlDataSource() throws SQLException {
final SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriver(new com.mysql.cj.jdbc.Driver());
dataSource.setUrl("jdbc:mysql://localhost:3306/task4");
dataSource.setUsername("name");
dataSource.setPassword("password");
return dataSource;
}
}
And I have Batch configuration class, but I dont know #EnableBatchProcessing annotation works without springboot
#Configuration
#EnableBatchProcessing
#Import(DataSourceConfiguration.class)
public class BatchConfiguration {
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
#Autowired
private DataSource dataSource;
#Bean
public ItemReader<User> itemReader() {
return new JdbcCursorItemReaderBuilder<User>()
.dataSource(this.dataSource)
.name("creditReader")
.sql("select userLogin, userEmail, userPassword, accountBalance from userInfo")
.rowMapper(new UserRowMapper())
.build();
}
#Bean
public ItemWriter<User> simpleItemWriter(){
return new SimpleUserWriter();
}
#Bean
public Job sampleJob() {
return this.jobs.get("sampleJob")
.start(step1())
.build();
}
#Bean
public Step step1() {
return this.steps.get("step1")
.<User,User>chunk(10)
.reader(itemReader())
.writer(simpleItemWriter())
.build();
}
}
Main class contains those lines of code :
public class Demo {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new AnnotationConfigApplicationContext(BatchConfiguration.class);
context.getBean("jobRepository");
JobLauncher jobLauncher = context.getBean("jobLauncher",JobLauncher.class);
Job job = context.getBean("sampleJob", Job.class);
try {
JobExecution execution = jobLauncher.run(job, new JobParameters());
System.out.println("Job Exit Status : "+ execution.getStatus());
} catch (JobExecutionException e) {
System.out.println("Job ExamResult failed");
e.printStackTrace();
}
}
}
How can I solve this problem?Thanks for helping

The error seems to say that you are missing the tables needed by Spring batch.
Check https://docs.spring.io/spring-batch/docs/4.3.x/reference/html/schema-appendix.html
You can also find SQL files ready for the different DB engine under org/springframework/bacth/core/schema-<DB>.sql in the spring batch core jar file

Related

BeanCurrentlyInCreationException when setting DataSource in Spring Boot and Batch project

I am using both Spring Boot and Batch in a maven multi-module project for parsing CSV files and storing data in a MySQL database.
When running the batch module using my BatchLauncher class (shared below) I get a BeanCurrentlyInCreationException caused by getDataBase() which I use for configuring my MySQL database. (click this link to see logs)
And when I remove this method Spring Boot choose automatically an embedded database of type H2 (link for logs)
BatchLauncher class :
#Slf4j
public class BatchLauncher {
public static void main(String[] args) {
try {
Launcher.launchWithConfig("My Batch", BatchConfig.class, false);
}catch (Exception ex) {
log.error(ex.getMessage());
}
}
}
Launcher class :
#Slf4j
public class Launcher {
private Launcher() {}
public static void launchWithConfig(String batchName, Class<?> configClass, boolean oncePerDayMax) throws JobExecutionException, BatchException {
try {
// Check the spring profiles used
log.info("Start batch \"" + batchName + "\" with profiles : " + System.getProperty("spring.profiles.active"));
// Load configuration
#SuppressWarnings("resource")
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configClass);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job job = context.getBean(Job.class);
//Authorize only one execution of each job per day
JobParameters jobParameters = new JobParameters();
JobExecution execution = jobLauncher.run(job, jobParameters);
if(!BatchStatus.COMPLETED.equals(execution.getStatus())) {
throw new BatchException("Unknown error while executing batch : " + batchName);
}
}catch (Exception ex){
log.error("Exception",ex);
throw new BatchException(ex.getMessage());
}
}
}
BatchConfig class :
#Slf4j
#Configuration
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
#EnableBatchProcessing
#ComponentScan(basePackages = {
"fr.payet.flad.batch.tasklet",
"fr.payet.flad.batch.mapper"
})
#Import({CoreConfig.class})
public class BatchConfig {
private StepBuilderFactory steps;
private JobBuilderFactory jobBuilderFactory;
private ReadInputTasklet readInputTasklet;
public BatchConfig(StepBuilderFactory steps, JobBuilderFactory jobBuilderFactory, ReadInputTasklet readInputTasklet) {
this.steps = steps;
this.jobBuilderFactory = jobBuilderFactory;
this.readInputTasklet = readInputTasklet;
}
#Bean
public DataSource getDataBase(){
return DataSourceBuilder
.create()
.driverClassName("com.mysql.jdbc.Driver")
.url("jdbc:mysql://localhost:3306/myDb?useSSL=false")
.username("myuser")
.password("mypwd")
.build();
}
#Bean
public Step readInputStep() {
return steps.get("readInputStep")
.tasklet(readInputTasklet)
.build();
}
#Bean
public Job readCsvJob() {
return jobBuilderFactory.get("readCsvJob")
.incrementer(new RunIdIncrementer())
.flow(readInputStep())
.end()
.build();
}
}
The solution was to create a custom DataSourceConfiguration class annotated with #Configuration in which I set my own database like this :
#Bean
public DataSource getDataBase(){
return DataSourceBuilder
.create()
.driverClassName("com.mysql.jdbc.Driver")
.url("jdbc:mysql://localhost:3306/myDB?useSSL=false")
.username("myUser")
.password("myPwd")
.build();
}

spring batch 3 ItemReader reading CSV file data only one time

Hi I am new to Spring Batch and I referred this link to run the sample program.
I wanted to insert the large amount of CSV file data into MySQL database and through the example I am able to execute it fine with limited number of lines of CSV data.
But when I inserted more number of lines into CSV file and execute the batch program it was not inserting any data in other words it was not reading the file itself and not even showing any error/exception(s). I think I need to configure ItemReader to read for more than one time.
Following is the BatchConfiguration.java code
#Configuration
#EnableBatchProcessing
public class BatchConfiguration {
#Bean
public ItemReader<Student> reader() {
FlatFileItemReader<Student> reader = new FlatFileItemReader<Student>();
reader.setResource(new ClassPathResource("student-data.csv"));
reader.setLineMapper(new DefaultLineMapper<Student>() {{
setLineTokenizer(new DelimitedLineTokenizer() {{
setNames(new String[] {"stdId", "subMarkOne", "subMarkTwo" });
}});
setFieldSetMapper(new BeanWrapperFieldSetMapper<Student>() {{
setTargetType(Student.class);
}});
}});
return reader;
}
#Bean
public ItemWriter<Marksheet> writer(DataSource dataSource) {
JdbcBatchItemWriter<Marksheet> writer = new JdbcBatchItemWriter<Marksheet>();
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Marksheet>());
writer.setSql("INSERT INTO marksheet (studentId,totalMark) VALUES (:stdId,:totalSubMark)");
writer.setDataSource(dataSource);
return writer;
}
#Bean
public ItemProcessor<Student, Marksheet> processor() {
return new StudentItemProcessor();
}
#Bean
public Job createMarkSheet(JobBuilderFactory jobs, Step step) {
return jobs.get("createMarkSheet")
.flow(step)
.end()
.build();
}
#Bean
public Step step(StepBuilderFactory stepBuilderFactory, ItemReader<Student> reader,
ItemWriter<Marksheet> writer, ItemProcessor<Student, Marksheet> processor) {
return stepBuilderFactory.get("step")
.<Student, Marksheet> chunk(5)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
#Bean
public DataSource getDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/concretepage");
dataSource.setUsername("root");
dataSource.setPassword("root123");
return dataSource;
}
main class to run
#ComponentScan
#EnableAutoConfiguration
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Main.class, args);
List<Marksheet> result = ctx.getBean(JdbcTemplate.class).query("select studentId,totalMark FROM marksheet",
new RowMapper<Marksheet>() {
#Override
public Marksheet mapRow(ResultSet rs, int row) throws SQLException {
return new Marksheet(rs.getString(1), Integer.parseInt(rs.getString(2)));
}
});
System.out.println("Number of Record:"+result.size());
}
}
process class
public class StudentItemProcessor implements ItemProcessor<Student, Marksheet> {
#Override
public Marksheet process(final Student student) throws Exception {
int totalMark = student.getSubMarkOne()+student.getSubMarkTwo();
System.out.println("student id:"+student.getStdId() +" and Total mark:"+ totalMark);
Marksheet marksheet = new Marksheet(student.getStdId(), totalMark);
return marksheet;
}
}
Any help will be appreciated. thanks.

Spring Hibernate H2 Junit testing - how to load schema on start

I am trying to develop tests for my application (I found out about the tests very late...) and I am stuck and the basic configuration. I have googled through many examples and none of them satisfied me and frankly left me a bit confused.
What I am trying to achieve is to load an import.sql on start of the test (which is a dump file from existing MySQL schema) and load it into H2 database.
Here is the hibernate config file:
#Configuration
#EnableTransactionManagement
#ComponentScan({ "kamienica.feature" })
public class HibernateTestConfiguration {
#Autowired
ApplicationContext applicationContext;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "kamienica" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;"
+ "INIT=CREATE SCHEMA IF NOT EXISTS kamienica;DB_CLOSE_ON_EXIT=FALSE");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.hbm2ddl.auto", "update");
// this is where I tried to load script the first time:
// properties.put("hibernate.hbm2ddl.import_files", "kamienica.sql");
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
Everytime I start a test I get a message that:
org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata INFO:
HHH000262: Table not found: apartment
And I get empty/null values when trying to retrieve anything
I have tried to load sql file in the hibernate config (via hibernate properties) as well as in superclass which all my test classes are planned to extend:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { HibernateTestConfiguration.class })
public class BaseTest {
private EmbeddedDatabase db;
#Autowired
private SessionFactory sessionFactory;
//second attempt to load sql file
#Before
public void setUp() throws Exception {
db = new EmbeddedDatabaseBuilder().addScript("import.sql").build();
}
#After
public void tearDown() throws Exception {
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}
How can I load sql file and prepare the H2 database to perform the tests?
I hope this spring boot approach will help you. First create a resources directory (classpath for springboot)in the src/test directory at the root of your project.
In this directory, you will start placing your fixture SQL data files named say data.sql .
Then, create a application.properties file on the same level (same directory see screenshot). This file should be populated as shown here:
spring.datasource.url = jdbc:h2:~/test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
#spring.datasource.url: jdbc:mysql://localhost:3306/yourDB
#spring.datasource.username = root
#spring.datasource.password =
# Hibernate
hibernate.show_sql: true
#hibernate.dialect: org.hibernate.dialect.MySQL5Dialec
spring.jpa.hibernate.ddl-auto=none
Screenshot:
Now your tester method.
#RunWith(SpringJUnit4ClassRunner.class)
....
#Autowired
private DataSource ds; //your application.properties
#Autowired
private WebApplicationContext context;
private static boolean loadDataFixtures = true;
private MockMvc mockMvc;
....
#Before
public void setupMockMvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Before
public void loadDataFixtures() {
if (loadDataFixtures) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(context.getResource("classpath:/data.sql"));
DatabasePopulatorUtils.execute(populator, ds);
loadDataFixtures = false;
}
}
#Test
public void yourmethod() {
assertEquals(3, repository.count()); //example
}
Without any output or the complete stacktrace, the only I can suggest you is:
You aren't showing any #Test method. How are you getting that error?
Is your file import.sql in src/test/resources folder? (note the test path)
Is your sql script well formated? Have you tried to run once exported? Could you post the part of the sql script wich creates the apartment table ?
If all are true, maybe the problem is not about loading the sql but how it's used, or the content of the script, or the name of the tables, etc...
After a long 'investigation' I have concluded that the problem was hidden somewhere in he DBUnit, TestNG setup.
I decided to keep it simple and switched to JUnit tests.
In case others might have similar problems here is the config file that works for me:
#Configuration
#EnableTransactionManagement
#ComponentScan({ "kamienica.feature" })
public class JUnitConfig {
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "kamienica" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:kamienica;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.hbm2ddl.auto", "create");
return properties;
}
#Bean
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
All is needed now is to insert import.sql file in the resources folder.
I also found out that each insert statements must be in one line no matter how long it is, otherwise it won't be loaded.
Finally a simple test class:
#ContextConfiguration(classes = { JUnitConfig.class })
#RunWith(SpringJUnit4ClassRunner.class)
public class ApartmentServiceTest extends AbstractServiceTest{
#Autowired
ApartmentService service;
#Test
public void getList() {
List<Apartment> list = service.getList();
System.out.println(list.toString());
assertEquals(5, list.size());
}
}

How can I use Spring Batch with OSGI

I want to use Spring batch with osgi to run a job daily.
here what i did:
#Component
#EnableBatchProcessing
public class BatchConfiguration {
private JobBuilderFactory jobs;
public JobBuilderFactory getJobs() {
return jobs;
}
public void setJobs(JobBuilderFactory jobs) {
this.jobs = jobs;
}
private StepBuilderFactory steps;
private EmployeeRepository employeeRepository; //spring data repository
public EmployeeRepository getEmployeeRepository() {
return employeeRepository;
}
#Reference
public void setEmployeeRepository(EmployeeRepository employeeRepository) {
employeeRepository= employeeRepository;
}
public Step syncEmployeesStep() throws Exception{
RepositoryItemWriter writer = new RepositoryItemWriter();
writer.setRepository(employeeRepository);
writer.setMethodName("save");
return steps.get("syncEmployeesStep")
.<Employee, Employee> chunk(10)
.reader(reader())
.writer(writer)
.build();
}
public Job importEmpJob()throws Exception {
return jobs.get("importEmpJob")
.incrementer(new RunIdIncrementer())
.start(syncEmployeesStep())
.next(syncEmployeesStep())
.build();
}
public ItemReader<Employee> reader() throws Exception {
String jpqlQuery = "select a from Employee a";
ServerEMF entityManager = new ServerEMF();
JpaPagingItemReader<Employee> reader = new JpaPagingItemReader<Tariff>();
reader.setQueryString(jpqlQuery);
reader.setEntityManagerFactory(entityManager.getEntityManagerFactory());
reader.setPageSize(3);
reader.afterPropertiesSet();
reader.setSaveState(true);
return reader;
}
}
here I want to run this job to sync between two databases,My problem is how to run this job inside osgi.
#EnableScheduling
#Component
public class JobRunner {
private JobLauncher jobLauncher;
private Job job ;
private BatchConfiguration batchConfig;
//private JobBuilderFactory jobs;
//private JobRepository jobrepo;
final static Logger logger = LoggerFactory.getLogger(BatchConfiguration.class);
BundleContext ctx;
#SuppressWarnings("rawtypes")
ServiceTracker servicetracker;
#Activate
public void start(BundleContext context) {
batchConfig = new BatchConfiguration();
//jobs = new JobBuilderFactory(jobRepository)
try {
job = batchConfig.importEmpJob(); //job is null because i don't know how to use it
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ctx = context;
servicetracker= new ServiceTracker(ctx, BatchConfiguration.class, null);
servicetracker.open();
new Thread() {
public void run() { findAndRunJob(); }
}.start();
}
#Deactivate
public void stop() {
configAdminTracker.close();
}
#Scheduled(fixedRate = 5000)
protected void findAndRunJob() {
logger.info("job created.");
try {
String dateParam = new Date().toString();
JobParameters param = new JobParametersBuilder().addString("date", dateParam).toJobParameters();
System.out.println(dateParam);
JobExecution execution = jobLauncher.run(job, param);
System.out.println("Exit Status : " + execution.getStatus());
} catch (Exception e) {
//e.printStackTrace();
}
}
for sure,I got a java.lang.NullPointerException because the job is null.
could anyone help me with that?
after updates
#Component
#EnableBatchProcessing
public class BatchConfiguration {
private EmployeeRepository employeeRepository; //spring data repository
public EmployeeRepository getEmployeeRepository() {
return employeeRepository;
}
#Reference
public void setEmployeeRepository(EmployeeRepository employeeRepository) {
employeeRepository= employeeRepository;
}
public Step syncEmployeesStep() throws Exception{
RepositoryItemWriter writer = new RepositoryItemWriter();
writer.setRepository(employeeRepository);
writer.setMethodName("save");
return steps.get("syncEmployeesStep")
.<Employee, Employee> chunk(10)
.reader(reader())
.writer(writer)
.build();
}
public Job importEmpJob(JobRepository jobRepository, PlatformTransactionManager transactionManager)throws Exception {
JobBuilderFactory jobs= new JobBuilderFactory(jobRepository);
StepBuilderFactory stepBuilderFactory = new StepBuilderFactory(jobRepository, transactionManager);
return jobs.get("importEmpJob")
.incrementer(new RunIdIncrementer())
.start(syncEmployeesStep())
.next(syncEmployeesStep())
.build();
}
public ItemReader<Employee> reader() throws Exception {
String jpqlQuery = "select a from Employee a";
ServerEMF entityManager = new ServerEMF();
JpaPagingItemReader<Employee> reader = new JpaPagingItemReader<Tariff>();
reader.setQueryString(jpqlQuery);
reader.setEntityManagerFactory(entityManager.getEntityManagerFactory());
reader.setPageSize(3);
reader.afterPropertiesSet();
reader.setSaveState(true);
return reader;
}
}
job runner class
private JobLauncher jobLauncher;
private PlatformTransactionManager transactionManager;
private JobRepository jobRepository;
Job importEmpJob;
private BatchConfiguration batchConfig;
#SuppressWarnings("deprecation")
#Activate
public void start(BundleContext context) {
try {
batchConfig = new BatchConfiguration();
this.transactionManager = new ResourcelessTransactionManager();
MapJobRepositoryFactoryBean repositorybean = new MapJobRepositoryFactoryBean();
repositorybean.setTransactionManager(transactionManager);
this.jobRepository = repositorybean.getJobRepository(); //error after executing this statement
// setup job launcher
SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
simpleJobLauncher.setTaskExecutor(new SyncTaskExecutor());
simpleJobLauncher.setJobRepository(jobRepository);
this.jobLauncher = simpleJobLauncher;
//System.out.println(job);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ctx = context;
configAdminTracker = new ServiceTracker(ctx, BatchConfiguration.class.getName(), null);
configAdminTracker.open();
new Thread() {
public void run() { findAndRunJob(); }
}.start();
}
#Deactivate
public void stop() {
configAdminTracker.close();
}
protected void findAndRunJob() {
logger.info("job created.");
try {
String dateParam = new Date().toString();
// creating the job
Job job = batchConfig.importEmpJob(jobRepository, transactionManager);
// running the job
JobExecution execution = this.jobLauncher.run(job, new JobParameters());
System.out.println("Exit Status : " + execution.getStatus());
} catch (Exception e) {
//e.printStackTrace();
}
}
what i getting is "java.lang.IllegalArgumentException: interface org.springframework.batch.core.repository.JobRepository is not visible from class loader" after running .could anyone help me with that error?
In Short
If you're just trying to kick off a something simple and don't need all the Spring Batch goodness I would look into the Apache Sling Commons Scheduler which has a simple job processor on top of Quartz for scheduling[1].
In General
There are a couple of considerations here depending on what you are trying to do. Are you deploying the Spring Batch Jars to the OSGi container with the assumption that the code that is written for the jobs (steps, tasks, etc) will live in separate bundles? OSGi's purpose is to develop modular code so my answer is assuming that this is your end goal. The folks at Pivotal have dropped requiring support for OSGi on their artifacts so to make it work you'll need to determine what you need to export from the Batch jar files. This can be done with BND. I would recommend checking out the new BND Maven plugin [2]. I would configure the Export-Package to export the interfaces you need to write the jobs so that you can write the jobs in separate modular bundles. Then I would probably embed the spring batch jars in a bundle and write a small wrapper around the JobLauncher. This should contain all the actual batch code to a single classloader so that you don't have to worry about OSGi trying to pull in classes dynamically. The downside is that this will prevent you from using many of the batch annotations outside of the spring batch bundle you created but will provide the modularity that you'd be looking for by implementing this type of solution with OSGi.
[1] https://sling.apache.org/documentation/bundles/apache-sling-eventing-and-job-handling.html
[2] http://njbartlett.name/2015/03/27/announcing-bnd-maven-plugin.html

NullPointerException trying to Autowire Service in Java Application (Spring)

I am writing a Java Application that is going to be using Spring, Hibernate and more its going to be packaged in a Jar and run from the command like.
My main class looks like the following right now:
public class App
{
private static final Logger logger = LoggerFactory.getLogger(App.class);
#Autowired
private static MemberInquiryService memberInquiryService;
public static void main(String[] args )
{
logger.info("Starting Inquiry Batch Process");
int pendingRecords = memberInquiryService.getPendingRecordCount();
logger.info("Current Number Of Pendinig Records (" + pendingRecords + ")");
logger.info("Ending Inquiry Batch Process");
}
}
and in the getPendingRecordCount I am just return "10" for testing:
public int getPendingRecordCount()
{
return 10;
};
Why would I be getting the following error:
Exception in thread "main" java.lang.NullPointerException
at org.XXXX.inquirybatch.app.App.main(App.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Also here is my DatabaseConfig.class
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages= { "org.xxxx.inquirybatch", "org.xxxx.core" })
#PropertySource("classpath:application.properties")
public class DatabaseConfig {
private static final Logger logger = LoggerFactory.getLogger(DatabaseConfig.class);
#Autowired
Environment env;
#Bean
public DataSource dataSource() {
String serverType = env.getProperty("server.type");
try {
if(serverType.equalsIgnoreCase("tomcat"))
{
com.mchange.v2.c3p0.ComboPooledDataSource ds = new com.mchange.v2.c3p0.ComboPooledDataSource();
ds.setDriverClass(env.getProperty("database.driver"));
ds.setUser(env.getProperty("database.user"));
ds.setPassword(env.getProperty("database.password"));
ds.setJdbcUrl(env.getProperty("database.url"));
return ds;
}
else
{
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:jboss/datasources/mySQLDB");
}
}
catch (Exception e)
{
logger.error(e.getMessage());
}
return null;
}
#Bean
public SessionFactory sessionFactory()
{
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setHibernateProperties(getHibernateProperties());
factoryBean.setPackagesToScan(new String[] { "org.xxxx.inquirybatch.model", "org.xxxx.core.model" } );
try {
factoryBean.afterPropertiesSet();
} catch (IOException e) {
logger.error(e.getMessage());
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return factoryBean.getObject();
}
#Bean
public Properties getHibernateProperties()
{
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
hibernateProperties.setProperty("hibernate.use_sql_comments", env.getProperty("hibernate.use_sql_comments"));
hibernateProperties.setProperty("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
hibernateProperties.setProperty("javax.persistence.validation.mode", env.getProperty("javax.persistence.validation.mode"));
//Audit History flags
hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", env.getProperty("org.hibernate.envers.store_data_at_delete"));
hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", env.getProperty("org.hibernate.envers.global_with_modified_flag"));
return hibernateProperties;
}
#Bean
public HibernateTransactionManager hibernateTransactionManager()
{
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory());
htm.afterPropertiesSet();
return htm;
}
}
Spring never injects static fields. And it only inject objects that are retrieved from the application context, or are themselves injected into other objects.
You're not even creating an application context in your program, so Spring doesn't play any role in this program.
I suggest to read the documentation.
You need to get the memberInquiryService from ClassPathXmlApplicationContext
For example :-
ApplicationContext context= new ClassPathXmlApplicationContext("spring config.xml");
MmberInquiryService memberInquiryService = context.getBean("memberInquiryService ");
basically in your code snippet MemberInquiryService is not spring managed as you are not getting from spring container. Also you need to declare MmberInquiryService entry in spring config.xml
I had to change the main class to..
public static void main(String[] args )
{
logger.info("Starting Inquiry Batch Process");
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MemberInquiryService memberInquiryService = (MemberInquiryService) context.getBean("memberInquiryService");
int pendingRecords = memberInquiryService.getPendingRecordCount();
logger.info("Current Number Of Pendinig Records (" + pendingRecords + ")");
logger.info("Ending Inquiry Batch Process");
}

Categories