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
Spring Batch Creating Customized reader by extending JdbcCursorItemReader
提问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 RowMapper
and has @Component("myRowMapper")
. Similarly, MyPrepStmntSetter implements PreparedStatementSetter
and 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:
配置:
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>
batch-jobs.xml:
<import resource="classpath:/META-INF/spring/batch/jobs/myJob.xml" />
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" />
在批处理 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>
批处理jobs.xml:
<import resource="classpath:/META-INF/spring/batch/jobs/myJob.xml" />
在 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 dataSource
field in your MyJdbcReader
class. Actually, you're not overriding the dataSource
field 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 dataSource
field 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 dataSource
field to be set. But in your case, it's not! Just because you're wiring your DataSource
to a field that has nothing to do with the dataSource
in 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 setDataSource
definition like the following:
并更改如下setDataSource
定义:
@Override
@Resource
public void setDataSource(DataSource dataSource){
super.setDataSource(dataSource);
}
and remove the getDataSource
definition. It will fail, as you won't have a dataSource
variable in scope anymore.
并删除getDataSource
定义。它会失败,因为您将不再有dataSource
范围内的变量。