The method stream(ItemStream) in the type AbstractTaskletStepBuilder<SimpleStepBuilder<Customer,Customer>> is not applicable for the arguments ( - spring

How to classify the elements using Spring Batch ? I want to write data into two different tables or files for now doing this in the console.
Error:
The method stream(ItemStream) in the type AbstractTaskletStepBuilder> is not applicable for the arguments (ItemWriter)
#EnableBatchProcessing
#SpringBootApplication
public class ClassifierCompositeItemApplication {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
#Value("classpath:input/customer.csv")
private Resource inputResource;
public ClassifierCompositeItemApplication(JobBuilderFactory jobs, StepBuilderFactory steps) {
this.jobBuilderFactory = jobs;
this.stepBuilderFactory = steps;
}
#Bean
#StepScope
public FlatFileItemReader<Customer> classifierCompositeWriterItemReader() {
return new FlatFileItemReaderBuilder<Customer>()
.name("customerFileReader")
.resource(inputResource)
.delimited()
.names(new String[] { "firstName", "middleInitial", "lastName", "address", "city", "state", "zip" })
.targetType(Customer.class)
.build();
}
#Bean
public ClassifierCompositeItemWriter<Customer> compositeItemWriter() throws IOException {
final Classifier<Customer, ItemStreamWriter<? super Customer>> classifier = new CustomerClassifier(
this.customer1(), this.customer2());
return new ClassifierCompositeItemWriterBuilder<Customer>()
.classifier(classifier)
.build();
}
#Bean
#StepScope
public ItemStreamWriter<Customer> customer1() throws IOException {
System.out.println("Customer #1");
return new ItemStreamWriter<Customer>() {
#Override
public void open(ExecutionContext executionContext) throws ItemStreamException {
}
#Override
public void update(ExecutionContext executionContext) throws ItemStreamException {
}
#Override
public void close() throws ItemStreamException {
}
#Override
public void write(List<? extends Customer> items) throws Exception {
for (Customer customer : items) {
System.out.println(customer);
}
}
};
}
#Bean
public ItemStreamWriter<NewCustomer> customer2() {
System.out.println("Customer #2");
return new ItemStreamWriter<NewCustomer>() {
#Override
public void open(ExecutionContext executionContext) throws ItemStreamException {
}
#Override
public void update(ExecutionContext executionContext) throws ItemStreamException {
}
#Override
public void close() throws ItemStreamException {
}
#Override
public void write(List<? extends NewCustomer> items) throws Exception {
for (NewCustomer customer : items) {
System.out.println(customer);
}
}
};
}
#Bean
public Step classifierCompositeWriterStep() throws IOException {
return this.stepBuilderFactory.get("compositeWriterStep")
.<Customer, Customer>chunk(10)
.reader(this.classifierCompositeWriterItemReader())
.writer(this.compositeItemWriter())
.stream(this.customer1())
.stream(this.customer2())
.build();
}
#Bean
public Job classifierCompositeWriterJob() throws IOException {
return this.jobBuilderFactory.get("compositeWriterJob")
.start(this.classifierCompositeWriterStep())
.build();
}
public static void main(String[] args) {
SpringApplication.run(ClassifierCompositeItemApplication.class, args);
}
}
CustomerClassifier.java
#Data
public class CustomerClassifier implements Classifier<Customer, ItemStreamWriter<? super Customer>> {
private static final long serialVersionUID = 1L;
private final ItemStreamWriter<Customer> fileItemWriter;
private final ItemStreamWriter<Customer> jdbcItemWriter;
#Override
public ItemStreamWriter<? super Customer> classify(Customer customer) {
if (customer.getState().matches("^[A-M].*")) {
return fileItemWriter;
} else {
return jdbcItemWriter;
}
}
}
Customer.java
#Data
public class Customer implements Serializable {
private String firstName;
private String middleInitial;
private String lastName;
private String address;
private String city;
private String state;
private String zip;
#Override
public String toString() {
return "Customer{" + ", firstName='" + firstName + '\'' + ", middleInitial='" + middleInitial + '\''
+ ", lastName='" + lastName + '\'' + ", address='" + address + '\'' + ", city='" + city + '\''
+ ", state='" + state + '\'' + ", zip='" + zip + '\'' + '}';
}
}
Schema.sql
CREATE TABLE springbatch.TBL_CUSTOMER_WRITER (
firstname varchar NULL,
middleinitial varchar NULL,
lastname varchar NULL,
address varchar NULL,
city varchar NULL,
state varchar NULL,
zipcode varchar NULL
)
WITH (
OIDS=FALSE
) ;
Customer.csv
Richard,N,Darrow,5570 Isabella Ave,St. Louis,IL,58540
Barack,G,Donnelly,7844 S. Greenwood Ave,Houston,CA,38635
Ann,Z,Benes,2447 S. Greenwood Ave,Las Vegas,NY,55366
Laura,9S,Minella,8177 4th Street,Dallas,FL,04119
Erica,Z,Gates,3141 Farnam Street,Omaha,CA,57640
Warren,L,Darrow,4686 Mt. Lee Drive,St. Louis,NY,94935
Warren,M,Williams,6670 S. Greenwood Ave,Hollywood,FL,37288
Harry,T,Smith,3273 Isabella Ave,Houston,FL,97261
Steve,O,James,8407 Infinite Loop Drive,Las Vegas,WA,90520
Erica,Z,Neuberger,513 S. Greenwood Ave,Miami,IL,12778
Aimee,C,Hoover,7341 Vel Avenue,Mobile,AL,35928
Jonas,U,Gilbert,8852 In St.,Saint Paul,MN,57321
Regan,M,Darrow,4851 Nec Av.,Gulfport,MS,33193
Stuart,K,Mckenzie,5529 Orci Av.,Nampa,ID,18562
Sydnee,N,Robinson,894 Ornare. Ave,Olathe,KS,25606

You are registering your delegate item writers as streams here:
.stream(this.customer1())
.stream(this.customer2())
But those are not ItemStreams. You need to change the return type of the methods customer1() and customer2() to ItemStreamWriter.
EDIT: Add an example:
import java.util.Arrays;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ClassifierCompositeItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.item.support.builder.ClassifierCompositeItemWriterBuilder;
import org.springframework.classify.Classifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableBatchProcessing
public class MyJob {
#Bean
public ItemReader<Customer> itemReader() {
return new ListItemReader<>(Arrays.asList(new Customer("foo"), new Customer("bar")));
}
#Bean
public ItemWriter<Customer> fooWriter() {
return items -> {
for (Customer item : items) {
System.out.println("foo writer: item " + item.name);
}
};
}
#Bean
public ItemWriter<Customer> barWriter() {
return items -> {
for (Customer item : items) {
System.out.println("bar writer: item " + item.name);
}
};
}
#Bean
public ClassifierCompositeItemWriter<Customer> classifierCompositeItemWriter() {
final Classifier<Customer, ItemWriter<? super Customer>> classifier =
new CustomerClassifier(this.fooWriter(), this.barWriter());
return new ClassifierCompositeItemWriterBuilder<Customer>()
.classifier(classifier)
.build();
}
#Bean
public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) {
return jobs.get("job")
.start(steps.get("step")
.<Customer, Customer>chunk(5)
.reader(itemReader())
.writer(classifierCompositeItemWriter())
.build())
.build();
}
public static void main(String[] args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job job = context.getBean(Job.class);
jobLauncher.run(job, new JobParameters());
}
static class Customer {
String name;
public Customer(String name) {
this.name = name;
}
}
static class CustomerClassifier implements Classifier<Customer, ItemWriter<? super Customer>> {
private ItemWriter<? super Customer> fooItemWriter;
private ItemWriter<? super Customer> barItemWriter;
public CustomerClassifier(ItemWriter<? super Customer> fooItemWriter, ItemWriter<? super Customer> barItemWriter) {
this.fooItemWriter = fooItemWriter;
this.barItemWriter = barItemWriter;
}
#Override
public ItemWriter<? super Customer> classify(Customer customer) {
return customer.name.startsWith("f") ? fooItemWriter : barItemWriter;
}
}
}
This prints:
foo writer: item foo
bar writer: item bar

Related

java.lang.IllegalStateException: Failed to execute - CommandLineRunner NullPointer Excaption

Repository
package com.example.demo.jpa;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.example.demo.entity.Person;
#Repository
#Transactional
public class RepositoryJpa {
#PersistenceContext
EntityManager entityManager;
//#Autowired
Person person = new Person();
public Person findById(int id) {
System.out.println("value of parameter id is "+id);
return entityManager.find(Person.class,id);
}
public Person update(Person person) {
return entityManager.merge(person);
}
}
Entity table:
package com.example.demo.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
//#Table(name="person")
public class Person {
#Id
private int id;
private String name;
private String location;
private String birth_date;
// public Person(int i, String string, String string2, String string3) {}
public Person(int id, String name, String location, String birth_date) {
// super();
this.id = id;
this.name = name;
this.location = location;
this.birth_date = birth_date;
}
public Person(String name, String location, String birth_date) {
// super();
this.name = name;
this.location = location;
this.birth_date = birth_date;
}
public Person() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
System.out.println("i called setID=" + this.id);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("i called setName=" + this.name);
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
System.out.println("i called setLocation=" + this.location);
}
public String getBirth_date() {
return birth_date;
}
public void setBirth_date(String birth_date) {
this.birth_date = birth_date;
// System.out.println("i called setBirth_ID="+this.birth_date);
}
/*
* #Override public String toString() { return "Person [id=" + id + ", name=" +
* name + ", location=" + location + ", birth_date=" + birth_date + ", getId()="
* + getId() + ", getName()=" + getName() + ", getLocation()=" + getLocation() +
* ", getBirth_date()=" + getBirth_date() + "]"; }
*/
}
SpringBootApplication
package com.example.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import com.example.demo.entity.Person;
import com.example.demo.jpa.RepositoryJpa;
#SpringBootApplication
public class JpaDemoApplication implements CommandLineRunner {
private Logger logger=LoggerFactory.getLogger(this.getClass());
//#Autowired
Person person = new Person();
RepositoryJpa repo = new RepositoryJpa();
public static void main(String[] args) {
// TODO Auto-generated method stub
SpringApplication.run(JpaDemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
System.out.println("hello all");
logger.info("find by id -> {}", repo.findById(2));
logger.info("insert user-> {}",repo.update(new Person(5,"gaurav","chicago","05/06/82")));
}
}
Application Properties
spring.h2.console.enabled=true
#spring.datasource.url=jdbc:h2:mem:testdb
#spring.batch.job.enabled=false
spring.jpa.show-sql=true
##EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
spring.jpa.defer-datasource-initialization=true
I'm a beginner to SpringBoot and I don't know what wrong I'm doing related to entity manager method it giving the error of and also #Autowired not working, I'm manually creating the object!
Help me please!!!
Console error screenshot also shared below please check:
Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:780) ~[spring-boot-2.6.4.jar:2.6.4]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:761) ~[spring-boot-2.6.4.jar:2.6.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-2.6.4.jar:2.6.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.4.jar:2.6.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.4.jar:2.6.4]
at com.example.demo.JpaDemoApplication.main(JpaDemoApplication.java:31) ~[classes/:na]
Caused by: java.lang.NullPointerException: null
at com.example.demo.jpa.RepositoryJpa.update(RepositoryJpa.java:31) ~[classes/:na]
at com.example.demo.JpaDemoApplication.run(JpaDemoApplication.java:46) ~[classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:777) ~[spring-boot-2.6.4.jar:2.6.4]
... 5 common frames omitted
console
You should Autowire the repository bean in JpaDemoApplication class, you should never create an object for spring component using new keyword
#SpringBootApplication
public class JpaDemoApplication implements CommandLineRunner {
private Logger logger=LoggerFactory.getLogger(this.getClass());
#Autowired
RepositoryJpa repositoryJpa;
public static void main(String[] args) {
// TODO Auto-generated method stub
SpringApplication.run(JpaDemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
System.out.println("hello all");
logger.info("find by id -> {}", repo.findById(2));
logger.info("insert user-> {}",repo.update(new Person(5,"gaurav","chicago","05/06/82")));
}
}

Spring Batch - How to read from One Table and Write Data into two different table

I'm using Spring Boot and Spring Batch to read data from One table of source database table and split the data and write it into two tables of target database.
I choose to use CompositeItemWriter for this, but CompositeItemWriter<?> only one type. I want to write few fields in one table and other fields into another table.
Say: OLD Customer and NEW Customer.
Error:
The constructor CustomerClassifier(JdbcBatchItemWriter, JdbcBatchItemWriter) is undefined
ClassifierCompositeItemApplication.java
#EnableBatchProcessing
#SpringBootApplication
public class ClassifierCompositeItemApplication {
private JobBuilderFactory jobBuilderFactory;
private StepBuilderFactory stepBuilderFactory;
public ClassifierCompositeItemApplication(JobBuilderFactory jobs, StepBuilderFactory steps) {
this.jobBuilderFactory = jobs;
this.stepBuilderFactory = steps;
}
#Value("classpath:input/customer.csv")
private Resource inputResource;
#Bean
#StepScope
public FlatFileItemReader<Customer> classifierCompositeWriterItemReader() {
return new FlatFileItemReaderBuilder<Customer>()
.name("customerFileReader")
.resource(inputResource).delimited()
.names(new String[] { "firstName", "middleInitial", "lastName", "address", "city", "state", "zip" })
.targetType(Customer.class)
.build();
}
#Bean
public ClassifierCompositeItemWriter<Customer> compositeItemWriter() throws IOException {
final Classifier<Customer, ItemWriter<? super Customer>> classifier = new CustomerClassifier(
this.customer1(null), this.customer2(null));
return new ClassifierCompositeItemWriterBuilder<Customer>().classifier(classifier).build();
}
#Bean
public JdbcBatchItemWriter<Customer> customer1(DataSource dataSource) {
return new JdbcBatchItemWriterBuilder<Customer>()
.namedParametersJdbcTemplate(new NamedParameterJdbcTemplate(dataSource))
.sql("INSERT INTO TBL_CUSTOMER_WRITER (firstname, middleinitial, lastname, address, city, " + "state, "
+ "zipcode) " + "VALUES(:firstName, " + ":middleInitial, " + ":lastName, " + ":address, "
+ ":city, " + ":state, " + ":zip)")
.beanMapped().build();
}
#Bean
public JdbcBatchItemWriter<NewCustomer> customer2(DataSource dataSource) {
return new JdbcBatchItemWriterBuilder<NewCustomer>()
.namedParametersJdbcTemplate(new NamedParameterJdbcTemplate(dataSource))
.sql("INSERT INTO TBL_CUSTOMER_WRITER (firstname, middleinitial, lastname, address, city, " + "state, "
+ "zipcode) " + "VALUES(:firstName, " + ":middleInitial, " + ":lastName, " + ":address, "
+ ":city, " + ":state, " + ":zip)")
.beanMapped().build();
}
#Bean
public Step classifierCompositeWriterStep() throws IOException {
return this.stepBuilderFactory.get("compositeWriterStep")
.<Customer, Customer>chunk(10)
.reader(this.classifierCompositeWriterItemReader())
.writer(this.compositeItemWriter())
.stream(this.customer1(null))
.stream(this.customer2(null))
.build();
}
#Bean
public Job classifierCompositeWriterJob() throws IOException {
return this.jobBuilderFactory.get("compositeWriterJob").start(this.classifierCompositeWriterStep()).build();
}
public static void main(String[] args) {
SpringApplication.run(ClassifierCompositeItemApplication.class, args);
}
}
CustomerClassifier.java
#AllArgsConstructor
public class CustomerClassifier implements Classifier<Customer, ItemWriter<? super Customer>> {
private static final long serialVersionUID = 1L;
private final ItemWriter<Customer> customer1;
private final ItemWriter<Customer> customer2;
#Override
public ItemWriter<? super Customer> classify(Customer customer) {
if (customer.getState().matches("^[A-M].*")) {
return customer1;
} else {
return customer2;
}
}
}
You can use a ClassifierCompositeItemWriter. This composite writer is designed to classify items and call a delegate item writer for each class.
So in your case, you can have am item writer for old customers and another one for new customers and warp them in a classifier item writer. You can use one of the Classifier implementations provided by Spring or create a custom one (for example an item processor cam flag your items as old/new and the classifier uses this flag to classify them).
EDIT: Add an example:
import java.util.Arrays;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ClassifierCompositeItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.item.support.builder.ClassifierCompositeItemWriterBuilder;
import org.springframework.classify.Classifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableBatchProcessing
public class MyJob {
#Bean
public ItemReader<Customer> itemReader() {
return new ListItemReader<>(Arrays.asList(new Customer("foo"), new Customer("bar")));
}
#Bean
public ItemWriter<Customer> fooWriter() {
return items -> {
for (Customer item : items) {
System.out.println("foo writer: item " + item.name);
}
};
}
#Bean
public ItemWriter<Customer> barWriter() {
return items -> {
for (Customer item : items) {
System.out.println("bar writer: item " + item.name);
}
};
}
#Bean
public ClassifierCompositeItemWriter<Customer> classifierCompositeItemWriter() {
final Classifier<Customer, ItemWriter<? super Customer>> classifier =
new CustomerClassifier(this.fooWriter(), this.barWriter());
return new ClassifierCompositeItemWriterBuilder<Customer>()
.classifier(classifier)
.build();
}
#Bean
public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) {
return jobs.get("job")
.start(steps.get("step")
.<Customer, Customer>chunk(5)
.reader(itemReader())
.writer(classifierCompositeItemWriter())
.build())
.build();
}
public static void main(String[] args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job job = context.getBean(Job.class);
jobLauncher.run(job, new JobParameters());
}
static class Customer {
String name;
public Customer(String name) {
this.name = name;
}
}
static class CustomerClassifier implements Classifier<Customer, ItemWriter<? super Customer>> {
private ItemWriter<? super Customer> fooItemWriter;
private ItemWriter<? super Customer> barItemWriter;
public CustomerClassifier(ItemWriter<? super Customer> fooItemWriter, ItemWriter<? super Customer> barItemWriter) {
this.fooItemWriter = fooItemWriter;
this.barItemWriter = barItemWriter;
}
#Override
public ItemWriter<? super Customer> classify(Customer customer) {
return customer.name.startsWith("f") ? fooItemWriter : barItemWriter;
}
}
}
This prints:
foo writer: item foo
bar writer: item bar

Spring Batch Reader is reading alternate records

I have created a sample spring batch application which is trying to read record from a DB and in writer, it displays those records. However, I could see that only even numbered (alternate) records are printed.
It's not the problem of database as the behavior is consistent with both H2 database or Oracle database.
There are total 100 records in my DB.
With JDBCCursorItemReader, only 50 records are read and that too alternate one as can be seen from log snapshot
With JdbcPagingItemReader, only 5 records are read and that too alternate one as can be seen from log snapshot
My code configurations are given below. Why reader is skipping odd numbered records?
#Bean
public ItemWriter<Safety> safetyWriter() {
return items -> {
for (Safety item : items) {
log.info(item.toString());
}
};
}
#Bean
public JdbcCursorItemReader<Safety> cursorItemReader() throws Exception {
JdbcCursorItemReader<Safety> reader = new JdbcCursorItemReader<>();
reader.setSql("select * from safety " );
reader.setDataSource(dataSource);
reader.setRowMapper(new SafetyRowMapper());
reader.setVerifyCursorPosition(false);
reader.afterPropertiesSet();
return reader;
}
#Bean
JdbcPagingItemReader<Safety> safetyPagingItemReader() throws Exception {
JdbcPagingItemReader<Safety> reader = new JdbcPagingItemReader<>();
reader.setDataSource(dataSource);
reader.setFetchSize(10);
reader.setRowMapper(new SafetyRowMapper());
H2PagingQueryProvider queryProvider = new H2PagingQueryProvider();
queryProvider.setSelectClause("*");
queryProvider.setFromClause("safety");
Map<String, Order> sortKeys = new HashMap<>(1);
sortKeys.put("id", Order.ASCENDING);
queryProvider.setSortKeys(sortKeys);
reader.setQueryProvider(queryProvider);
return reader;
}
#Bean
public Step importSafetyDetails() throws Exception {
return stepBuilderFactory.get("importSafetyDetails")
.<Safety, Safety>chunk(chunkSize)
//.reader(cursorItemReader())
.reader(safetyPagingItemReader())
.writer(safetyWriter())
.listener(new StepListener())
.listener(new ChunkListener())
.build();
}
#Bean
public Job job() throws Exception {
return jobBuilderFactory.get("job")
.start(importSafetyDetails())
.build();
}
Domain classes looks like below:
#NoArgsConstructor
#AllArgsConstructor
#Data
public class Safety {
private int id;
}
public class SafetyRowMapper implements RowMapper<Safety> {
#Override
public Safety mapRow(ResultSet resultSet, int i) throws SQLException {
if(resultSet.next()) {
Safety safety = new Safety();
safety.setId(resultSet.getInt("id"));
return safety;
}
return null;
}
}
#SpringBootApplication
#EnableBatchProcessing
public class SpringBatchSamplesApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchSamplesApplication.class, args);
}
}
application.yml configuration is as below:
spring:
application:
name: spring-batch-samples
main:
allow-bean-definition-overriding: true
datasource:
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
driver-class-name: org.h2.Driver
hikari:
connection-timeout: 20000
maximum-pool-size: 10
h2:
console:
enabled: true
batch:
initialize-schema: never
server:
port: 9090
sqls are as below:
CREATE TABLE safety (
id int NOT NULL,
CONSTRAINT PK_ID PRIMARY KEY (id)
);
INSERT INTO safety (id) VALUES (1);
...100 records are inserted
Listeners classes are as below:
#Slf4j
public class StepListener{
#AfterStep
public ExitStatus afterStep(StepExecution stepExecution) {
log.info("In step {} ,Exit Status: {} ,Read Records: {} ,Committed Records: {} ,Skipped Read Records: {} ,Skipped Write Records: {}",
stepExecution.getStepName(),
stepExecution.getExitStatus().getExitCode(),
stepExecution.getReadCount(),
stepExecution.getCommitCount(),
stepExecution.getReadSkipCount(),
stepExecution.getWriteSkipCount());
return stepExecution.getExitStatus();
}
}
#Slf4j
public class ChunkListener {
#BeforeChunk
public void beforeChunk(ChunkContext context) {
log.info("<< Before the chunk");
}
#AfterChunk
public void afterChunk(ChunkContext context) {
log.info("<< After the chunk");
}
}
I tried to reproduce your problem, but I couldn't. Maybe it would be great if you could share more code.
Meanwhile I created a simple job to read 100 records from "safety" table a print them to the console. And it is working fine.
.
#SpringBootApplication
#EnableBatchProcessing
public class ReaderWriterProblem implements CommandLineRunner {
#Autowired
DataSource dataSource;
#Autowired
StepBuilderFactory stepBuilderFactory;
#Autowired
JobBuilderFactory jobBuilderFactory;
#Autowired
private JobLauncher jobLauncher;
#Autowired
private ApplicationContext context;
public static void main(String[] args) {
String[] arguments = new String[]{LocalDateTime.now().toString()};
SpringApplication.run(ReaderWriterProblem.class, arguments);
}
#Bean
public ItemWriter<Safety> safetyWriter() {
return new ItemWriter<Safety>() {
#Override
public void write(List<? extends Safety> items) throws Exception {
for (Safety item : items) {
//log.info(item.toString());
System.out.println(item);
}
}
};
}
// #Bean
// public JdbcCursorItemReader<Safety> cursorItemReader() throws Exception {
// JdbcCursorItemReader<Safety> reader = new JdbcCursorItemReader<>();
//
// reader.setSql("select * from safety ");
// reader.setDataSource(dataSource);
// reader.setRowMapper(new SafetyRowMapper());
// reader.setVerifyCursorPosition(false);
// reader.afterPropertiesSet();
//
// return reader;
// }
#Bean
JdbcPagingItemReader<Safety> safetyPagingItemReader() throws Exception {
JdbcPagingItemReader<Safety> reader = new JdbcPagingItemReader<>();
reader.setDataSource(dataSource);
reader.setFetchSize(10);
reader.setRowMapper(new SafetyRowMapper());
PostgresPagingQueryProvider queryProvider = new PostgresPagingQueryProvider();
queryProvider.setSelectClause("*");
queryProvider.setFromClause("safety");
Map<String, Order> sortKeys = new HashMap<>(1);
sortKeys.put("id", Order.ASCENDING);
queryProvider.setSortKeys(sortKeys);
reader.setQueryProvider(queryProvider);
return reader;
}
#Bean
public Step importSafetyDetails() throws Exception {
return stepBuilderFactory.get("importSafetyDetails")
.<Safety, Safety>chunk(5)
//.reader(cursorItemReader())
.reader(safetyPagingItemReader())
.writer(safetyWriter())
.listener(new MyStepListener())
.listener(new MyChunkListener())
.build();
}
#Bean
public Job job() throws Exception {
return jobBuilderFactory.get("job")
.listener(new JobListener())
.start(importSafetyDetails())
.build();
}
#Override
public void run(String... args) throws Exception {
JobParametersBuilder jobParametersBuilder = new JobParametersBuilder();
jobParametersBuilder.addString("date", LocalDateTime.now().toString());
try {
Job job = (Job) context.getBean("job");
jobLauncher.run(job, jobParametersBuilder.toJobParameters());
} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException | JobParametersInvalidException e) {
e.printStackTrace();
}
}
public static class JobListener implements JobExecutionListener {
#Override
public void beforeJob(JobExecution jobExecution) {
System.out.println("Before job");
}
#Override
public void afterJob(JobExecution jobExecution) {
System.out.println("After job");
}
}
private static class SafetyRowMapper implements RowMapper<Safety> {
#Override
public Safety mapRow(ResultSet resultSet, int i) throws SQLException {
Safety safety = new Safety();
safety.setId(resultSet.getLong("ID"));
return safety;
}
}
public static class MyStepListener implements StepExecutionListener {
#Override
public void beforeStep(StepExecution stepExecution) {
System.out.println("Before Step");
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
System.out.println("After Step");
return ExitStatus.COMPLETED;
}
}
private static class MyChunkListener implements ChunkListener {
#Override
public void beforeChunk(ChunkContext context) {
System.out.println("Before Chunk");
}
#Override
public void afterChunk(ChunkContext context) {
System.out.println("After Chunk");
}
#Override
public void afterChunkError(ChunkContext context) {
}
}
}
Hope this helps

JpaPagingItemReader - endless Loop on failed read

Hi I am getting endless loop when exception was thrown.
Is there something wrong to the transactionmanager I am using?
Thanks!
Below is my job setup
#Bean
public ItemReader<Entity> bizAppReader() {
#SuppressWarnings("serial")
Map<String, Object> map = new HashMap<String, Object>() {{
put("status", Arrays.asList(new ApplicationStatus[] {ApplicationStatus.COMPLETED, ApplicationStatus.PENDING, ApplicationStatus.INITIATE}));
}};
return new JpaPagingItemReaderBuilder<Entity>()
.name("bizAppJpaReader")
.entityManagerFactory(entityManagerFactory)
.queryString("from Entity b where b.status in (:status)").parameterValues(map)
.build();
}
#Bean
public Step bizApplicatonStep(#Value("${batch.chunk_size:10}") int chunkSize,
ItemReader<Entity> bizAppReader,
ItemProcessor<Entity, Map<ApplicationStatus, String>> bizAppProcessor,
ItemWriter<Map<ApplicationStatus, String>> bizAppWriter) {
return stepBuilderFactory.get("bizApplicatonStep")
.<Entity, Map<ApplicationStatus, String>>chunk(chunkSize).reader(bizAppReader)
.processor(bizAppProcessor).writer(bizAppWriter)
.faultTolerant()
.skip(Exception.class)
.noRollback(Exception.class)
.skipPolicy((e, i) -> {
log.error("Exception occurred : ", e);
return true;
})
.allowStartIfComplete(true)
.build();
}
#Slf4j
#SpringBootApplication
#EnableBatchProcessing
public class BatchApplication extends DefaultBatchConfigurer {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(BatchApplication.class, args);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job job;
JobParameters jobParameters;
if (ArrayUtils.isNotEmpty(args) && args[0].equals("bookingJob")) {
job = context.getBean("bookingJob", Job.class);
jobParameters = new JobParametersBuilder()
.addString("startMonth", args[1])
.addString("endMonth", args[2])
.toJobParameters();
} else {
job = context.getBean("bizApplicationJob", Job.class);
jobParameters = new JobParametersBuilder().toJobParameters();
}
JobExecution jobExecution = jobLauncher.run(job, jobParameters);
if (BatchStatus.FAILED.equals(jobExecution.getStatus())) {
log.error("job execution completed exit code is : 1 ");
System.exit(1);
}
log.info("job execution completed exit code is : 0 ");
}
#Override
protected JobRepository createJobRepository() throws Exception {
return new MapJobRepositoryFactoryBean(new ResourcelessTransactionManager()).getObject();
}
}
Stacktrace:
10:13:20.657 [main] ERROR c.u.pweb.bizacct.batch.BizJobConfig - Exception occurred :
java.lang.IllegalStateException: Transaction already active
at org.hibernate.engine.transaction.internal.TransactionImpl.begin(TransactionImpl.java:52)
-===================== Updated application to replicate =====================
this happen if the reader failed to read with invalid Enum value from DB.
#Slf4j
#SpringBootApplication
#EnableBatchProcessing
public class DemoBatchApplication extends DefaultBatchConfigurer {
public static void main(String[] args) {
SpringApplication.run(DemoBatchApplication.class, args);
}
#Override
public void setDataSource(DataSource dataSource) {
super.setDataSource(null);
}
private final EntityManagerFactory entityManagerFactory;
public DemoBatchApplication(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
#Override
public PlatformTransactionManager getTransactionManager() {
return new JpaTransactionManager(this.entityManagerFactory);
}
}
enum
public enum ApplicationStatus {
MA, AS, SH, CP, AA, AP
}
table
#Entity
#Table(name = "APP_TBL_TEST")
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class ApplicationEntity {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
#Column(name = "ID", length = 50)
private String id;
#Column(name = "APPLICATION_NAME", unique = true, length = 50)
private String referenceNumber;
#Column(name = "STATUS")
#Enumerated(EnumType.STRING)
private ApplicationStatus status;
}
batch config
#Configuration
#Slf4j
#Data
public class ApplicationBatchCfg {
#Autowired
public EntityManagerFactory entityManagerFactory;
#Autowired
public JobBuilderFactory jobBuilderFactory;
#Autowired
public StepBuilderFactory stepBuilderFactory;
#Bean
public Step applicationStep(#Value("${batch.chunk_size:5}") int chunkSize,
ItemReader<ApplicationEntity> appReader,
ItemProcessor<ApplicationEntity, String> appProcessor,
ItemWriter<String> appWriter){
return stepBuilderFactory.get("applicatonStep")
.<ApplicationEntity, String>chunk(chunkSize)
.reader(appReader)
.processor(appProcessor)
.writer(appWriter)
.faultTolerant()
.skip(Exception.class)
.noRollback(Exception.class)
.skipPolicy((e, i) -> {
log.error("Exception occurred : ", e);
return true;
})
.skipLimit(5)
.allowStartIfComplete(true)
.build();
}
#Bean
public Job applicationJob(Step applicationStep) {
return jobBuilderFactory.get("applicationJob").incrementer(new RunIdIncrementer())
.start(applicationStep).build();
}
#Bean
public ItemReader<ApplicationEntity> appReader() {
return new JpaPagingItemReaderBuilder<ApplicationEntity>()
.name("appReader")
.entityManagerFactory(entityManagerFactory)
.queryString("from ApplicationEntity")
.pageSize(3)
.build();
}
#Bean
public ItemProcessor<ApplicationEntity, String> appProcessor(){
return new ItemProcessor<ApplicationEntity, String>() {
#Override
public String process(ApplicationEntity item) throws Exception {
//nothing for demo only
log.info("item {}", item);
return null;
}
};
}
#Bean
public ItemWriter<String> appWriter() {
return new ItemWriter<String>() {
#Override
public void write(List<? extends String> items) throws Exception {
//nothing for demo only
}
};
}
}
inside yml
spring:
jpa:
properties:
hibernate:
show_sql: true
use_sql_comments : true
format_sql: true
dialect: org.hibernate.dialect.Oracle10gDialect
datasource:
useWallet: false
url: jdbc:oracle:thin:#DB12397:1521:azd
platform: oracle
username: user
password: pwd

GWT + Spring + Hiberante. With no reason at all

I'm learning how to integrate Spring with GWT and RequestFactory by doing this following example. I got a NullPointerException and I don't know why. Can anyone help me?
Here is my code:
#Repository
public class EmployeeDAO implements IEmployeeDAO {
#PersistenceContext
private EntityManager entity;
#Override
public Employee findById(Long id) {
Query query = entity.createQuery("from Employee where id = :param");
query.setParameter("param", id);
query.setMaxResults(1);
return (Employee) query.getSingleResult();
}
#Transactional(propagation = Propagation.REQUIRED)
#Override
public void save(Employee employee) {
entity.merge(employee);
}
#Override
public void remove(Employee employee) {
entity.remove(employee);
}
#SuppressWarnings("unchecked")
#Override
public List<Employee> getAllEmployee() {
Query query = entity.createQuery("from Employee");
return query.getResultList();
}
// ...
}
and:
#Service(value = IEmployeeDAO.class, locator = DaoLocator.class)
public interface EmployeeRequestContext extends RequestContext {
Request<EmployeeProxy> findById(Long id);
Request<Void> save(EmployeeProxy employee);
Request<Void> remove(EmployeeProxy employee);
Request<List<EmployeeProxy>> getAllEmployee();
Request<EmployeeProxy> findOneByName(String name);
}
and:
#ProxyFor(Employee.class)
public interface EmployeeProxy extends EntityProxy {
Long getId();
String getName();
String getSurname();
void setId(Long id);
void setName(String name);
void setSurname(String surname);
Long getVersion();
void setVersion(Long version);
}
The NullPointerException is throw in GWT Entry Point in method:
protected void refresh() {
context = createFactory().employeeRequest();
final EmployeeProxy ep = context.create(EmployeeProxy.class);
ep.setName("Jan");
ep.setSurname("Kowalski");
ep.setVersion(new Long(0));
context.save(ep).fire(new Receiver<Void>() {
#Override
public void onSuccess(Void response) {
employeeList.add(ep);
}
#Override
public void onFailure(ServerFailure error) {
System.out.println("error podczas zapisu");
}
});
context = createFactory().employeeRequest();
context.getAllEmployee().fire(new Receiver<List<EmployeeProxy>>() {
#Override
public void onSuccess(List<EmployeeProxy> response) {
System.out.println(" " + response); // NULL
}
#Override
public void onFailure(ServerFailure error) {
}
});
System.out.println("Bedziemy wyswietlac dane!");
updateTable(employeeList);
}
the last one: method which create Factory:
private static EmployeeRequestFactory createFactory() {
EmployeeRequestFactory factory = GWT.create(EmployeeRequestFactory.class);
factory.initialize(new SimpleEventBus());
return factory;
}
Please help me...
Please print the stacktrace for the NullPointerException. Only then can we analyze the cause for the exception.

Resources