问题
I am developing Spring Batch CompositeItemReader and Writter
example. In this program I am trying to read data two tables from mysql db table and write to single XML file. When trying to do that I see following error is coming:
java.lang.ClassCastException: java.lang.String cannot be cast to com.common.batch.model.Customer
at com.common.batch.processor.CustomerProcessor.process(CustomerProcessor.java:1)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:126)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.transform(SimpleChunkProcessor.java:293)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:192)
at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:75)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:271)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:77)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:368)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:144)
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257)
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:198)
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)
at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:64)
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67)
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:162)
at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:141)
at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:134)
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:304)
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:135)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128)
at com.common.batch.main.CompositeXMLMain.main(CompositeXMLMain.java:24)
I don't have any clue in solving this issue. Please guide me. Employee.java
public class Employee implements Serializable{
private static final long serialVersionUID = 1L;
private Integer employeeNumber;
private String lastName;
private String firstName;
private String extension;
private String email;
private String officeCode;
private Integer reportsTo;
private String jobTitle;
// setters and getters
@Override
public String toString() {
return customerNumber + "|" + customerName + "|" + contactLastName + "|" + contactFirstName + "|" +
phone+ "|" + addressLine1 + "|" + addressLine2 + "|" + city + "|" + state+ "|" + postalCode + "|" +
country+ "|" + salesRepEmployeeNumber + "|" + creditLimit;
}
}
Customer.java
public class Customer implements Serializable{
private static final long serialVersionUID = 1L;
private Integer customerNumber;
private String customerName;
private String contactLastName;
private String contactFirstName;
private String phone;
private String addressLine1;
private String addressLine2;
private String city;
private String state;
private String postalCode;
private String country;
private Integer salesRepEmployeeNumber;
private Double creditLimit;
// setters and getters
@Override
public String toString() {
return employeeNumber + "|"+ lastName + "|" + firstName + "|"+ extension +
"|" + email + "|" + officeCode+ "|" + reportsTo + "|" + jobTitle;
}
}
xml-jdbc-composite-item-reader-job.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
<import resource="classpath:context-datasource.xml" />
<!-- JobRepository and JobLauncher are configuration/setup classes -->
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" />
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<!-- Step will need a transaction manager -->
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<!-- =========================================================== -->
<job id="compositeJdbcReaderJob" xmlns="http://www.springframework.org/schema/batch">
<step id="compositeJdbcReaderStep" next="compositeJdbcReaderStep2">
<tasklet>
<chunk reader="compositeItemReader1" writer="itemWriter1" processor="itemCustomerProcessor"
commit-interval="5" />
</tasklet>
</step>
<step id="compositeJdbcReaderStep2">
<tasklet>
<chunk reader="compositeItemReader2" writer="itemWriter2" processor="itemEmployeeProcessor"
commit-interval="5" />
</tasklet>
</step>
</job>
<!-- ============= Composite Item Reader ================ -->
<bean id="compositeItemReader1" class="com.common.batch.reader.CompositeCursorItemReader">
<property name="unifyingMapper">
<bean class="com.common.batch.mapper.DefaultUnifyingStringItemsMapper" />
</property>
<property name="cursorItemReaders">
<list>
<ref bean="itemReader1" />
</list>
</property>
</bean>
<bean id="compositeItemReader2" class="com.common.batch.reader.CompositeCursorItemReader">
<property name="unifyingMapper">
<bean class="com.common.batch.mapper.DefaultUnifyingStringItemsMapper" />
</property>
<property name="cursorItemReaders">
<list>
<ref bean="itemReader2" />
</list>
</property>
</bean>
<!-- ========== ItemReader =============== -->
<bean id="itemReader1" class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="dataSource" />
<property name="saveState" value="true" />
<property name="sql">
<value>
<![CDATA[ ${select.sql.customers} ]]>
</value>
</property>
<property name="rowMapper">
<bean class="com.common.batch.mapper.CustomerMapper" />
</property>
</bean>
<bean id="itemReader2" class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="dataSource" />
<property name="saveState" value="true" />
<property name="sql">
<value>
<![CDATA[ ${select.sql.employees} ]]>
</value>
</property>
<property name="rowMapper">
<bean class="com.common.batch.mapper.EmployeeMapper" />
</property>
</bean>
<!-- ItemWritter -->
<bean id="itemWriter1" class="org.springframework.batch.item.xml.StaxEventItemWriter">
<property name="resource" value="file:xml/customers.xml" />
<property name="marshaller" ref="customerUnmarshaller" />
<property name="rootTagName" value="customers" />
</bean>
<bean id="itemWriter2" class="org.springframework.batch.item.xml.StaxEventItemWriter">
<property name="resource" value="file:xml/customers.xml" />
<property name="marshaller" ref="employeeUnmarshaller" />
<property name="rootTagName" value="employees" />
</bean>
<!-- ======= Employee Unmarshaller ======== -->
<bean id="employeeUnmarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases">
<util:map id="aliases">
<entry key="employee" value="com.common.batch.model.Employee" />
</util:map>
</property>
</bean>
<!-- ======= Customer Unmarshaller ======== -->
<bean id="customerUnmarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases">
<util:map id="aliases">
<entry key="customer" value="com.common.batch.model.Customer" />
</util:map>
</property>
</bean>
<bean id="itemCustomerProcessor" class="com.common.batch.processor.CustomerProcessor" />
<bean id="itemEmployeeProcessor" class="com.common.batch.processor.EmployeeProcessor" />
</beans>
CompositeXMLMain.java
public class CompositeXMLMain {
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("composite/xml-jdbc-composite-item-reader-job.xml");
JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
Job job = (Job) context.getBean("compositeJdbcReaderJob");
JobExecution execution;
try {
execution = jobLauncher.run(job, new JobParameters());
System.out.println("Job Exit Status : "+ execution.getStatus());
} catch (JobExecutionAlreadyRunningException | JobRestartException
| JobInstanceAlreadyCompleteException | JobParametersInvalidException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
System.out.println("Done !!");
}
}
Edit-1: CustomerProcessor.java
public class CustomerProcessor implements ItemProcessor<Customer, Customer>{
@Override
public Customer process(Customer result) throws Exception {
System.out.println("Processing result :"+result);
return result;
}
}
EmployeeProcessor.java
public class EmployeeProcessor implements ItemProcessor<Employee, Employee>{
@Override
public Employee process(Employee result) throws Exception {
System.out.println("Processing result :"+result);
return result;
}
}
Edit-2: CustomerMapper.java
public class CustomerMapper implements RowMapper<Customer>{
@Override
public Customer mapRow(ResultSet rs, int rowNum) throws SQLException {
Customer customer = new Customer();
customer.setCustomerNumber(rs.getInt("customerNumber"));
customer.setCustomerName(rs.getString("customerName"));
customer.setContactLastName(rs.getString("contactLastName"));
customer.setContactFirstName(rs.getString("contactFirstName"));
customer.setPhone(rs.getString("phone"));
customer.setAddressLine1(rs.getString("addressLine1"));
customer.setAddressLine2(rs.getString("addressLine2"));
customer.setCity(rs.getString("city"));
customer.setState(rs.getString("state"));
customer.setPostalCode(rs.getString("postalCode"));
customer.setCountry(rs.getString("country"));
customer.setSalesRepEmployeeNumber(rs.getInt("salesRepEmployeeNumber"));
customer.setCreditLimit(rs.getDouble("creditLimit"));
return customer;
}
}
EmployeeMapper.java
public class EmployeeMapper implements RowMapper<Employee>{
@Override
public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
Employee employee = new Employee();
employee.setEmployeeNumber(rs.getInt("employeeNumber"));
employee.setLastName(rs.getString("lastName"));
employee.setFirstName(rs.getString("firstName"));
employee.setExtension(rs.getString("extension"));
employee.setEmail(rs.getString("email"));
employee.setOfficeCode(rs.getString("officeCode"));
employee.setReportsTo(rs.getInt("reportsTo"));
employee.setJobTitle(rs.getString("jobTitle"));
return employee;
}
}
Edit-3:
I see couple of issue listed below:
- I see very weird XML output shown below
- I only getting second database tables data, first tables data overriding
XML Output?
<?xml version="1.0" encoding="UTF-8"?>
<employees>
<string>1002|Murphy|Diane|x5800|dmurphy@classicmodelcars.com|1|0|President</string>
<string>1056|Patterson|Mary|x4611|mpatterso@classicmodelcars.com|1|1002|VP Sales</string>
<string>1076|Firrelli|Jeff|x9273|jfirrelli@classicmodelcars.com|1|1002|VP Marketing</string>
<string>1088|Patterson|William|x4871|wpatterson@classicmodelcars.com|6|1056|Sales Manager (APAC)</string>
<string>1102|Bondur|Gerard|x5408|gbondur@classicmodelcars.com|4|1056|Sale Manager (EMEA)</string>
<string>1143|Bow|Anthony|x5428|abow@classicmodelcars.com|1|1056|Sales Manager (NA)</string>
<string>1165|Jennings|Leslie|x3291|ljennings@classicmodelcars.com|1|1143|Sales Rep</string>
<string>1166|Thompson|Leslie|x4065|lthompson@classicmodelcars.com|1|1143|Sales Rep</string>
<string>1188|Firrelli|Julie|x2173|jfirrelli@classicmodelcars.com|2|1143|Sales Rep</string>
<string>1216|Patterson|Steve|x4334|spatterson@classicmodelcars.com|2|1143|Sales Rep</string>
<string>1286|Tseng|Foon Yue|x2248|ftseng@classicmodelcars.com|3|1143|Sales Rep</string>
<string>1323|Vanauf|George|x4102|gvanauf@classicmodelcars.com|3|1143|Sales Rep</string>
<string>1337|Bondur|Loui|x6493|lbondur@classicmodelcars.com|4|1102|Sales Rep</string>
<string>1370|Hernandez|Gerard|x2028|ghernande@classicmodelcars.com|4|1102|Sales Rep</string>
<string>1401|Castillo|Pamela|x2759|pcastillo@classicmodelcars.com|4|1102|Sales Rep</string>
<string>1501|Bott|Larry|x2311|lbott@classicmodelcars.com|7|1102|Sales Rep</string>
<string>1504|Jones|Barry|x102|bjones@classicmodelcars.com|7|1102|Sales Rep</string>
<string>1611|Fixter|Andy|x101|afixter@classicmodelcars.com|6|1088|Sales Rep</string>
<string>1612|Marsh|Peter|x102|pmarsh@classicmodelcars.com|6|1088|Sales Rep</string>
<string>1619|King|Tom|x103|tking@classicmodelcars.com|6|1088|Sales Rep</string>
<string>1621|Nishi|Mami|x101|mnishi@classicmodelcars.com|5|1056|Sales Rep</string>
<string>1625|Kato|Yoshimi|x102|ykato@classicmodelcars.com|5|1621|Sales Rep</string>
<string>1702|Gerard|Martin|x2312|mgerard@classicmodelcars.com|4|1102|Sales Rep</string>
</employees>
Edit-4: I've corrected
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
<import resource="classpath:context-datasource.xml" />
<!-- JobRepository and JobLauncher are configuration/setup classes -->
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" />
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<!-- Step will need a transaction manager -->
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<!-- =========================================================== -->
<job id="compositeJdbcReaderJob" xmlns="http://www.springframework.org/schema/batch">
<step id="compositeJdbcReaderStep" next="compositeJdbcReaderStep2">
<tasklet>
<chunk reader="compositeItemReader1" writer="itemWriter1" commit-interval="5" />
</tasklet>
</step>
<step id="compositeJdbcReaderStep2">
<tasklet>
<chunk reader="compositeItemReader2" writer="itemWriter2" commit-interval="5" />
</tasklet>
</step>
</job>
<!-- ============= Composite Item Reader ================ -->
<bean id="compositeItemReader1" class="com.common.batch.reader.CompositeCursorItemReader">
<property name="unifyingMapper">
<bean class="com.common.batch.mapper.DefaultUnifyingStringItemsMapper" />
</property>
<property name="cursorItemReaders">
<list>
<ref bean="itemReader1" />
</list>
</property>
</bean>
<bean id="compositeItemReader2" class="com.common.batch.reader.CompositeCursorItemReader">
<property name="unifyingMapper">
<bean class="com.common.batch.mapper.DefaultUnifyingStringItemsMapper" />
</property>
<property name="cursorItemReaders">
<list>
<ref bean="itemReader2" />
</list>
</property>
</bean>
<!-- ========== ItemReader =============== -->
<bean id="itemReader1" class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="dataSource" />
<property name="saveState" value="true" />
<property name="sql">
<value>
<![CDATA[ ${select.sql.customers} ]]>
</value>
</property>
<property name="rowMapper">
<bean class="com.common.batch.mapper.CustomerMapper" />
</property>
</bean>
<bean id="itemReader2" class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="dataSource" />
<property name="saveState" value="true" />
<property name="sql">
<value>
<![CDATA[ ${select.sql.employees} ]]>
</value>
</property>
<property name="rowMapper">
<bean class="com.common.batch.mapper.EmployeeMapper" />
</property>
</bean>
<!-- ItemWritter -->
<bean id="itemWriter1" class="org.springframework.batch.item.xml.StaxEventItemWriter">
<property name="resource" value="file:xml/customers.xml" />
<property name="marshaller" ref="customerUnmarshaller" />
<property name="rootTagName" value="customers" />
</bean>
<bean id="itemWriter2" class="org.springframework.batch.item.xml.StaxEventItemWriter">
<property name="resource" value="file:xml/customers.xml" />
<property name="marshaller" ref="employeeUnmarshaller" />
<property name="rootTagName" value="employees" />
</bean>
<!-- ======= Employee Unmarshaller ======== -->
<bean id="employeeUnmarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases">
<util:map id="aliases">
<entry key="employee" value="com.common.batch.model.Employee" />
</util:map>
</property>
</bean>
<!-- ======= Customer Unmarshaller ======== -->
<bean id="customerUnmarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases">
<util:map id="aliases">
<entry key="customer" value="com.common.batch.model.Customer" />
</util:map>
</property>
</bean>
<bean id="itemCustomerProcessor" class="com.common.batch.processor.CustomerProcessor" />
<bean id="itemEmployeeProcessor" class="com.common.batch.processor.EmployeeProcessor" />
</beans>
回答1:
i do not understand, why you use CompositeItemReaders if you only use one reader for each, anyways
i guess you copied the DefaultUnifyingStringItemsMapper from this github repo?
source code there is:
public class DefaultUnifyingStringItemsMapper implements UnifyingItemsMapper<String> {
/** {@inheritDoc} */
@Override
public String mapItems(List<?> items) throws Exception {
if (items != null && items.size() > 0) {
StringBuilder sb = new StringBuilder();
for (Object item : items) {
if (item != null) {
sb.append(item);
}
}
if (sb.length() > 0) {
return sb.toString();
} else {
return null;
}
} else {
return null;
}
}
}
so that is the problem, it will just create a big string which is or is not the kind of item you actually want (to process in processor and writer), right now it won't work with the processor which expects items of type Employee
if you add more readers to the compositeitemreaders, you need to rethink your program especially the writers, what kind of item will they use?
来源:https://stackoverflow.com/questions/36389604/java-lang-classcastexception-java-lang-string-cannot-be-cast-to-com-common-batc