问题
I am writing one Spring Batch using Spring boot, and I need to write in two different tables based on conditions so I am trying to CompositeItemWriter however when I invoke the batch the writer is not getting called.
Here is my Job Configuration class.
@Configuration
public class JobConfiguration {
...
...
...
@Bean
public JdbcCursorItemReader<Notification> reader() {
JdbcCursorItemReader<Notification> reader = new JdbcCursorItemReader<Notification>();
reader.setDataSource(dataSource);
...
...
reader.setRowMapper(new BeanPropertyRowMapper<>(Notification.class));
return reader;
}
@Bean
public NotificationItemProcessor notificatonProcessor() {
return new NotificationItemProcessor();
}
@Bean
public JdbcBatchItemWriter<Notification> updateWriter() {
JdbcBatchItemWriter<Notification> writer = new JdbcBatchItemWriter<Notification>();
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Notification>());
...
writer.setDataSource(dataSource);
return writer;
}
/**
* Composite Exchange Writer
* @return
* @throws InstantiationException
* @throws IllegalAccessException
*/
@SuppressWarnings("unchecked")
@Bean
public CompositeItemWriter<Notification> compositeExchangeWriter() throws InstantiationException, IllegalAccessException {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put(ExchangeRouter.INSERT_EXCHANGE_FOR_NOTIFICATION.getActionName(), exchangeWorkflowWriter());
map.put(ExchangeRouter.INSERT_EXCHANGE_FOR_PACK.getActionName(), exchangeWriter());
map.put(ExchangeRouter.DO_NOTHING.getActionName(), doNothing());
return new CompositeItemWriterBuilder(map, ExchangeWriterRouterClassifier.class).build();
}
@Bean
public JdbcBatchItemWriter<Notification> exchangeWorkflowWriter() {
JdbcBatchItemWriter<Notification> writer = new JdbcBatchItemWriter<Notification>();
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Notification>());
writer.setSql(" INSERT INTO SOME TABLE..");
writer.setDataSource(dataSource);
return writer;
}
@Bean
public JdbcBatchItemWriter<Notification> exchangeWriter() {
JdbcBatchItemWriter<Notification> writer = new JdbcBatchItemWriter<Notification>();
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Notification>());
writer.setSql("INSERT INTO SOME OTHER TABLE.");
writer.setDataSource(dataSource);
return writer;
}
@Bean
public ItemWriter<Document> doNothing() {
return new DummyWriter();
}
@Bean
public Job generatePdf(JobCompletionNotificationListener listener) throws InstantiationException, IllegalAccessException {
return jobBuilderFactory.get("generatePdf")
.incrementer(new RunIdIncrementer())
.flow(treatStock())
.end()
.build();
}
@Bean
public Step treatStock() throws InstantiationException, IllegalAccessException {
return stepBuilderFactory.get("treatStock")
.<Notification, Notification>chunk(1)
.reader(reader())
.processor(notificatonProcessor())
.writer(compositeExchangeWriter())
.writer(updateWriter())
.build();
}
}
CompositeItemWriter.java
public class CompositeItemWriterBuilder extends CompositeItemBuilder<CompositeItemWriter> {
public CompositeItemWriterBuilder(HashMap<String, Object> matcherMap, Class<?> routerDelegate) throws InstantiationException, IllegalAccessException {
BackToBackPatternClassifier classif = new BackToBackPatternClassifier();
classif.setRouterDelegate(routerDelegate.newInstance());
classif.setMatcherMap(matcherMap);
ClassifierCompositeItemWriter classifier = new ClassifierCompositeItemWriter();
classifier.setClassifier(classif);
this.delegates.add(classifier);
}
public CompositeItemWriterBuilder(List<Object> delegates) {
this.delegates = delegates;
}
@Override
protected Class<?> getCompositeItem() {
return CompositeItemWriter.class;
}
}
CompositeItemBuiler.java
public abstract class CompositeItemBuilder<T> {
protected List<Object> delegates = new ArrayList<Object>();
@SuppressWarnings("unchecked")
public T build() throws InstantiationException, IllegalAccessException {
Object compositeItem = getCompositeItem().newInstance();
Method setDelegates = ReflectionUtils.findMethod(compositeItem.getClass(), "setDelegates", List.class);
ReflectionUtils.invokeMethod(setDelegates,compositeItem, delegates);
return (T) compositeItem;
}
abstract protected Class<?> getCompositeItem();
}
ExchangeWriterRouterClassifier .java (classify method is not getting called)
public class ExchangeWriterRouterClassifier {
@Classifier
public String classify(Notification notification) {
return notification.getExchangesWorkflow().getRouter().getActionName();
}
}
How does Spring calls Classifier ? Am I missing something?
回答1:
I am trying to CompositeItemWriter however when I invoke the batch the writer is not getting called.
The issue is in your step definition:
@Bean
public Step treatStock() throws InstantiationException, IllegalAccessException {
return stepBuilderFactory.get("treatStock")
.<Notification, Notification>chunk(1)
.reader(reader())
.processor(notificatonProcessor())
.writer(compositeExchangeWriter())
.writer(updateWriter())
.build();
}
You are calling the writer() method twice, hence the updateWriter() will override the compositeExchangeWriter(). You need to call the method once with the composite writer as parameter on which you would already set the delegate writers.
As a side note when using a composite writer, you need to make sure to register the delegates as streams if they don't implement the ItemStream interface. More details on this here: https://docs.spring.io/spring-batch/4.0.x/reference/html/readersAndWriters.html#delegatePatternAndRegistering
How does Spring calls Classifier ?
When a ClassifierCompositeItemWriter is correctly configured, Spring Batch will call the classifier on each item to determine which writer to use and then calls the appropriate writer to write the item.
In your configuration, the ClassifierCompositeItemWriter is not properly configured here:
@SuppressWarnings("unchecked")
public T build() throws InstantiationException, IllegalAccessException {
Object compositeItem = getCompositeItem().newInstance();
Method setDelegates = ReflectionUtils.findMethod(compositeItem.getClass(), "setDelegates", List.class);
ReflectionUtils.invokeMethod(setDelegates,compositeItem, delegates);
return (T) compositeItem;
}
I would not use reflection to set the delegates. The issue is that you are expecting the method compositeExchangeWriter to register a ClassifierCompositeItemWriter but its return type is CompositeItemWriter. So the composite writer is not seen as a classifier.
You can find an example of how to use the ClassifierCompositeItemWriter here: https://github.com/spring-projects/spring-batch/blob/master/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/ClassifierCompositeItemWriterTests.java#L44
来源:https://stackoverflow.com/questions/51435285/using-compositeitemwriter-the-writer-or-classify-method-is-not-getting-called