无法在 spring 批次中写入 FlatFile - 只有对象引用正在写入平面文件

Unable to write to FlatFile in spring batch - only object reference are getting write to flat file

我正在处理 Spring Batch Composite Writer 示例。在这里,我尝试使用 compositeItemReader 读取两个表数据并使用 FlatFileItemWriter 写入 CSV 文件。当我在 运行 主程序时执行此操作时,我只看到如下所示的输出,它只给我对象引用而不是值。

com.common.batch.model.Customer@1615682
com.common.batch.model.Customer@176ad02
com.common.batch.model.Customer@5883fc
com.common.batch.model.Customer@1411cbf
com.common.batch.model.Customer@1c66b53
com.common.batch.model.Customer@f459a4
com.common.batch.model.Customer@293404
com.common.batch.model.Customer@115fae6
com.common.batch.model.Customer@b1298
com.common.batch.model.Customer@f1df22
com.common.batch.model.Customer@c13eca
com.common.batch.model.Customer@1015199
com.common.batch.model.Customer@1943319
com.common.batch.model.Customer@a6bf88
com.common.batch.model.Customer@1687e27
com.common.batch.model.Customer@1bbd50b
com.common.batch.model.Customer@9fd887
com.common.batch.model.Customer@40af64
com.common.batch.model.Customer@baff9a
com.common.batch.model.Customer@e2f388
com.common.batch.model.Customer@10472ac
com.common.batch.model.Customer@10480e1

我希望能成功看到所有字段值。 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;


    public Integer getEmployeeNumber() {
        return employeeNumber;
    }
    public void setEmployeeNumber(Integer employeeNumber) {
        this.employeeNumber = employeeNumber;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getExtension() {
        return extension;
    }
    public void setExtension(String extension) {
        this.extension = extension;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getOfficeCode() {
        return officeCode;
    }
    public void setOfficeCode(String officeCode) {
        this.officeCode = officeCode;
    }
    public Integer getReportsTo() {
        return reportsTo;
    }
    public void setReportsTo(Integer reportsTo) {
        this.reportsTo = reportsTo;
    }
    public String getJobTitle() {
        return jobTitle;
    }
    public void setJobTitle(String jobTitle) {
        this.jobTitle = jobTitle;
    }
}

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;


    public Integer getCustomerNumber() {
        return customerNumber;
    }
    public void setCustomerNumber(Integer customerNumber) {
        this.customerNumber = customerNumber;
    }
    public String getCustomerName() {
        return customerName;
    }
    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }
    public String getContactLastName() {
        return contactLastName;
    }
    public void setContactLastName(String contactLastName) {
        this.contactLastName = contactLastName;
    }
    public String getContactFirstName() {
        return contactFirstName;
    }
    public void setContactFirstName(String contactFirstName) {
        this.contactFirstName = contactFirstName;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public String getAddressLine1() {
        return addressLine1;
    }
    public void setAddressLine1(String addressLine1) {
        this.addressLine1 = addressLine1;
    }
    public String getAddressLine2() {
        return addressLine2;
    }
    public void setAddressLine2(String addressLine2) {
        this.addressLine2 = addressLine2;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    public String getPostalCode() {
        return postalCode;
    }
    public void setPostalCode(String postalCode) {
        this.postalCode = postalCode;
    }
    public String getCountry() {
        return country;
    }
    public void setCountry(String country) {
        this.country = country;
    }
    public Integer getSalesRepEmployeeNumber() {
        return salesRepEmployeeNumber;
    }
    public void setSalesRepEmployeeNumber(Integer salesRepEmployeeNumber) {
        this.salesRepEmployeeNumber = salesRepEmployeeNumber;
    }
    public Double getCreditLimit() {
        return creditLimit;
    }
    public void setCreditLimit(Double creditLimit) {
        this.creditLimit = creditLimit;
    }
}

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"
    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">

    <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="compositeItemReader" writer="itemWriter" commit-interval="5" />
            </tasklet>
        </step>

        <step id="compositeJdbcReaderStep2">
            <tasklet>
                <chunk reader="compositeItemReader2" writer="itemWriter2" commit-interval="5" />
            </tasklet>
        </step>
    </job>

    <bean id="compositeItemReader" 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>


    <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>


    <bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
        <property name="resource" value="${output.file.location}" />
        <!-- <property name="appendAllowed" value="true" /> -->
        <property name="lineAggregator">
            <bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator"/>
        </property>
    </bean>


    <bean id="itemWriter2" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
        <property name="resource" value="${output.file.location}" />
        <property name="appendAllowed" value="true" />
        <property name="lineAggregator">
            <bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator"/>
        </property>
    </bean>
</beans>

Main.java

public class CompositeMain {
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("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 !!");
    }
}

我希望平面文件应该像例子那样写:

firstValue | secondValue | thirdValue ...

根据 Michael 的建议,我开发了以下 XML 文件:

<bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
        <property name="resource" value="${output.file.location}" />
        <!-- <property name="appendAllowed" value="true" /> -->
        <property name="lineAggregator">
            <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
                <property name="delimiter" value="|" />
                <property name="fieldExtractor">
                    <!-- Extractor which returns the value of beans property through reflection -->
                    <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
                        <property name="names" value="customerNumber,customerName,contactLastName,contactFirstName,phone,addressLine1,addressLine2,city,state,postalCode,country,salesRepEmployeeNumber,creditLimit" />
                    </bean>
                </property>
            </bean>
        </property>
    </bean>


    <bean id="itemWriter2" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
        <property name="resource" value="${output.file.location}" />
        <property name="appendAllowed" value="true" />
       <property name="lineAggregator">
            <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
                <property name="delimiter" value="|" />
                <property name="fieldExtractor">
                    <!-- Extractor which returns the value of beans property through reflection -->
                    <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
                        <property name="names" value="employeeNumber,lastName,firstName,extension,email,officeCode,reportsTo,jobTitle" />
                    </bean>
                </property>
            </bean>
        </property>
    </bean>

您正在使用 PassThroughLineAggregator(强调我的)

The most basic implementation of the LineAggregator interface is the PassThroughLineAggregator, which simply assumes that the object is already a string, or that its string representation is acceptable for writing

更改为特定的 LineAggregator 或调整对象的 toString 方法以输出您想要的方式