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

标签 java spring datasource spring-batch autowired

我需要通过扩展 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;
    }


}

此外,MyRowMapper 实现了 RowMapper 并具有 @Component("myRowMapper")。类似地,MyPrepStmntSetter 实现了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;
    }




}

配置:

  1. 在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. 在 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" />
    

上述配置对于除 dataSource 之外的所有属性都运行良好。 IE。运行时,我得到:IllegalArgumentException DataSource必须提供

堆栈跟踪:

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$1.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

我哪里出错了?是否是由于混合了 XML 配置和组件扫描?

最佳答案

问题出在 MyJdbcReader 类中的 dataSource 字段。实际上,您并没有重写父类(super class)的 dataSource 字段,只是因为它是私有(private)的(在父类(super class)中)。请参阅source code on grepcode .

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

通过查看源代码(在上面的超链接中提到),您可以在第 150 行看到需要设置 dataSource 字段。但就你而言,事实并非如此!只是因为您将 DataSource 连接到与父类(super class)中的 dataSource 无关的字段。

我建议您重写 setter 方法(这是公共(public)的)并将注释移至 setter。像这样的事情:

删除这些行:

@Resource
DataSource dataSource;

并更改 setDataSource 定义,如下所示:

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

并删除getDataSource 定义。它将失败,因为作用域中不再有 dataSource 变量。

关于java - Spring Batch 通过扩展 JdbcCursorItemReader 创建自定义阅读器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21802440/

相关文章:

java - 给定类的完全限定名称,从命令行下载 jar 的最简单方法

java - 密码验证无法解析为类型错误

java - 我们可以使用普通的jdbc连接来进行Spring事务管理吗

java - 来自变量的 Spring 动态 HttpStatus

java - 将 Tomcat 中的 .war 应用程序部署到 root

java - 从服务器向所有客户端发送消息

java - wss 协议(protocol)的 Apache 反向代理

java - 当 2 个不同的版本位于 2 个单独的模块中时,JBoss 将不会使用正确模块中的 Oracle DataSource 驱动程序

ios - 从 TableView 数据源中删除之前删除确认警报

java - 没有设置数据源就无法启动 spring boot 应用程序