Java Spring Batch 通过扩展 JdbcCursorItemReader 创建自定义阅读器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21802440/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 10:44:13  来源:igfitidea点击:

Spring Batch Creating Customized reader by extending JdbcCursorItemReader

javaspringdatasourcespring-batchautowired

提问by user2971387

I need to make a customized reader by extending JdbcCursorItemReader. I am doing it as below:

我需要通过扩展 JdbcCursorItemReader 来制作一个定制的阅读器。我这样做如下:

package sample.peektry;

import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("myReader")
public class MyReader implements ItemReader<MyBean> {

    @Autowired
    MyPeekableReader myPeekableReader;

    public MyPeekableReader getMyPeekableReader() {
        return myPeekableReader;
    }

    public void setMyPeekableReader(MyPeekableReader myPeekableReader) {
        this.myPeekableReader = myPeekableReader;
    }

    @Override
    public MyBean read() throws Exception, UnexpectedInputException,
    ParseException, NonTransientResourceException {
        System.out.println(" I will peek and read... :)");
        return null;
    }

}

package sample.peektry;

import org.springframework.batch.item.support.SingleItemPeekableItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("myPeekableReader")
public class MyPeekableReader extends SingleItemPeekableItemReader<MyBean> {

    @Autowired
    private MyJdbcReader myJdbcReader;

    public MyJdbcReader getMyJdbcReader() {
        return myJdbcReader;
    }

    public void setMyJdbcReader(MyJdbcReader myJdbcReader) {
        this.myJdbcReader = myJdbcReader;
    }


}

Further, MyRowMapper implements RowMapperand has @Component("myRowMapper"). Similarly, MyPrepStmntSetter implements PreparedStatementSetterand has @Component("myPrepStmntSetter")

进一步,MyRowMapper implements RowMapper并且有@Component("myRowMapper")。同样,MyPrepStmntSetter implements PreparedStatementSetter并且有@Component("myPrepStmntSetter")

package sample.peektry;

import javax.annotation.Resource;
import javax.sql.DataSource;
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("myJdbcReader")
public class MyJdbcReader extends JdbcCursorItemReader<MyBean> {

    @Resource
    DataSource dataSource;

    @Autowired
    MyRowMapper myRowMapper;

    @Autowired
    MyPrepSetter myPrepSetter;

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public MyRowMapper getMyRowMapper() {
        return myRowMapper;
    }

    public void setMyRowMapper(MyRowMapper myRowMapper) {
        this.myRowMapper = myRowMapper;
    }

    public MyPrepSetter getMyPrepSetter() {
        return myPrepSetter;
    }

    public void setMyPrepSetter(MyPrepSetter myPrepSetter) {
        this.myPrepSetter = myPrepSetter;
    }




}

Configurations:

配置:

  1. in batch-infra.xml:

    <batch:job-repository id="jobRepository" data-source="dataSource"
                      transaction-manager="transactionManager" />
    
    <!-- connect to database -->
    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver" />
        <property name="url" value="jdbc:derby://localhost:1527/MyDB" />
    

    <bean id="jobLauncher"
          class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
        <property name="jobRepository" ref="jobRepository"></property>
    

    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    

  2. batch-jobs.xml:

    <import resource="classpath:/META-INF/spring/batch/jobs/myJob.xml" />
    
  3. in app-context.xml(after xmlns and all):

    <context:component-scan base-package="sample.peektry" />
    
    <import resource="classpath:/META-INF/spring/batch/batch-infra.xml" />
    
    <import resource="classpath:/META-INF/spring/batch/batch-jobs.xml" />
    
  1. 在批处理 infra.xml 中:

    <batch:job-repository id="jobRepository" data-source="dataSource"
                      transaction-manager="transactionManager" />
    
    <!-- connect to database -->
    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver" />
        <property name="url" value="jdbc:derby://localhost:1527/MyDB" />
    

    <bean id="jobLauncher"
          class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
        <property name="jobRepository" ref="jobRepository"></property>
    

    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    

  2. 批处理jobs.xml:

    <import resource="classpath:/META-INF/spring/batch/jobs/myJob.xml" />
    
  3. 在 app-context.xml 中(在 xmlns 之后):

    <context:component-scan base-package="sample.peektry" />
    
    <import resource="classpath:/META-INF/spring/batch/batch-infra.xml" />
    
    <import resource="classpath:/META-INF/spring/batch/batch-jobs.xml" />
    

The above configuration is working fine for all the properties except dataSource. I.e. on running, I am getting: IllegalArgumentException DataSource must be provided

上述配置适用于除 dataSource 之外的所有属性。即在跑步时,我得到:IllegalArgumentException DataSource must be provided

Stacktrace:

堆栈跟踪:

2014-02-16 14:56:07,195 INFO [org.springframework.context.support.ClassPathXmlApplicationContext] - <Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@c7e553: startup date [Sun Feb 16 14:56:07 IST 2014]; root of context hierarchy>
2014-02-16 14:56:07,291 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - <Loading XML bean definitions from class path resource [META-INF/spring/app-context.xml]>
2014-02-16 14:56:07,625 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - <Loading XML bean definitions from class path resource [META-INF/spring/batch/batch-infra.xml]>
2014-02-16 14:56:10,210 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - <Loading XML bean definitions from class path resource [META-INF/spring/batch/batch-jobs.xml]>
2014-02-16 14:56:12,766 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - <Loading XML bean definitions from class path resource [META-INF/spring/batch/jobs/myJob.xml]>
2014-02-16 14:56:12,872 INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - <Overriding bean definition for bean 'myJob': replacing [Generic bean: class [org.springframework.batch.core.configuration.xml.SimpleFlowFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] with [Generic bean: class [org.springframework.batch.core.configuration.xml.JobParserJobFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null]>
2014-02-16 14:56:13,115 INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - <Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1d332b: defining beans [myJdbcReader,myPeekableReader,myPrepSetter,myReader,myRowMapper,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.batch.core.scope.internalStepScope,org.springframework.beans.factory.config.CustomEditorConfigurer,org.springframework.batch.core.configuration.xml.CoreNamespacePostProcessor,jobRepository,dataSource,jobLauncher,transactionManager,step1,myJob]; root of factory hierarchy>
2014-02-16 14:56:13,215 INFO [org.springframework.jdbc.datasource.DriverManagerDataSource] - <Loaded JDBC driver: org.apache.derby.jdbc.ClientDriver>
2014-02-16 14:56:13,304 INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - <Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1d332b: defining beans [myJdbcReader,myPeekableReader,myPrepSetter,myReader,myRowMapper,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.batch.core.scope.internalStepScope,org.springframework.beans.factory.config.CustomEditorConfigurer,org.springframework.batch.core.configuration.xml.CoreNamespacePostProcessor,jobRepository,dataSource,jobLauncher,transactionManager,step1,myJob]; root of factory hierarchy>
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myJdbcReader' defined in file [C:\Users\user\Documents\workspace-sts-3.3.0.RELEASE\sb-listener-test\target\classes\sample\peektry\MyJdbcReader.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: DataSource must be provided
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1420)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:293)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:192)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at sample.peektry.MyMain.main(MyMain.java:16)
Caused by: java.lang.IllegalArgumentException: DataSource must be provided
    at org.springframework.util.Assert.notNull(Assert.java:112)
    at org.springframework.batch.item.database.AbstractCursorItemReader.afterPropertiesSet(AbstractCursorItemReader.java:150)
    at org.springframework.batch.item.database.JdbcCursorItemReader.afterPropertiesSet(JdbcCursorItemReader.java:107)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
    ... 12 more

Where I am getting wrong? Is it due to mixing XML configuration and component scan?

我哪里出错了?是因为混合了 XML 配置和组件扫描吗?

采纳答案by Andrei Nicusan

The problem is with your dataSourcefield in your MyJdbcReaderclass. Actually, you're not overriding the dataSourcefield of the superclass, simply because it is private (in the superclass). See source code on grepcode.

问题出dataSource在你MyJdbcReader班级的领域。实际上,您并没有覆盖dataSource超类的字段,仅仅因为它是私有的(在超类中)。请参阅grepcode 上的源代码

What you're doing is to declare a new package-private field, which is by no means connected to the dataSourcefield in the superclass. See inheritance principles in Java for more details.

您正在做的是声明一个新的包私有字段,该字段绝不连接到dataSource超类中的字段。有关更多详细信息,请参阅 Java 中的继承原则。

By looking to the source code (mentioned in the hyperlink above), you can see at line 150 that one expects the dataSourcefield to be set. But in your case, it's not! Just because you're wiring your DataSourceto a field that has nothing to do with the dataSourcein the superclass.

通过查看源代码(在上面的超链接中提到),您可以在第 150 行看到人们希望dataSource设置该字段。但在你的情况下,它不是!仅仅因为您将您DataSource的字段连接到与dataSource超类中的无关的字段。

What I'd suggest you is to override the setter method (which is public) and move the annotation to the setter. Something like this:

我建议您覆盖 setter 方法(这是公共的)并将注释移动到 setter。像这样的东西:

Remove these lines:

删除这些行:

@Resource
DataSource dataSource;

and change the setDataSourcedefinition like the following:

并更改如下setDataSource定义:

@Override
@Resource
public void setDataSource(DataSource dataSource){
    super.setDataSource(dataSource);
}

and remove the getDataSourcedefinition. It will fail, as you won't have a dataSourcevariable in scope anymore.

并删除getDataSource定义。它会失败,因为您将不再有dataSource范围内的变量。