Using CompositeItemWriter the writer or classify method is not getting called

…衆ロ難τιáo~ 提交于 2019-12-08 10:19:23

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!