【问题标题】:I can't populate H2 database with values form CSV file我无法使用 CSV 文件中的值填充 H2 数据库
【发布时间】:2022-01-24 12:05:27
【问题描述】:

我正在尝试使用 CSV 文件中的值填充 H2 数据库,如下所示:

@Component
public class DBWriterOrder implements ItemWriter<OrderEntity> {

private OrderRepository orderRepository;

@Autowired
public DBWriterOrder(OrderRepository orderRepository) {
    this.orderRepository = orderRepository;
}

@Override
public void write(List<? extends OrderEntity> orders) throws Exception {
    System.out.println("Data Saved for Orders: " + orders);
    orderRepository.saveAll(orders);

     }
}

@Component
public class ProcessorOrder implements ItemProcessor<OrderEntity, OrderEntity> {

public SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");

@Override
public OrderEntity process(OrderEntity orderEntity) throws Exception {

    Date deliveryDate = sdf.parse(orderEntity.getDeliveryDate().toString());
    long deliveryDateInMillis = deliveryDate.getTime();
    orderEntity.setDeliveryDate(deliveryDateInMillis);

    Date lastUpdated = sdf.parse(orderEntity.getLastUpdated().toString());
    long lastUpdatedInMillis = lastUpdated.getTime();
    orderEntity.setLastUpdated(lastUpdatedInMillis);


    return orderEntity;
    }
}

@Configuration
@EnableBatchProcessing
public class SpringBatchConfigOrder {

@Bean
public Job jobOrder(JobBuilderFactory jobBuilderFactory,
               StepBuilderFactory stepBuilderFactory,
               ItemReader<OrderEntity> itemReader,
               ItemProcessor<OrderEntity, OrderEntity> itemProcessor,
               ItemWriter<OrderEntity> itemWriter
) {

    Step step = stepBuilderFactory.get("ETL-file-load")
            .<OrderEntity, OrderEntity>chunk(100)
            .reader(itemReader)
            .processor(itemProcessor)
            .writer(itemWriter)
            .build();


    return jobBuilderFactory.get("ETL-Load")
            .incrementer(new RunIdIncrementer())
            .start(step)
            .build();
}

@Bean
public FlatFileItemReader<OrderEntity> itemReaderOrder() {

    FlatFileItemReader<OrderEntity> flatFileItemReader = new FlatFileItemReader<>();
    flatFileItemReader.setResource(new FileSystemResource("src/main/resources/orders.csv"));
    flatFileItemReader.setName("CSV-Reader");
    flatFileItemReader.setLinesToSkip(1);
    flatFileItemReader.setLineMapper(lineMapperOrder());
    return flatFileItemReader;
}

@Bean
public LineMapper<OrderEntity> lineMapperOrder() {

    DefaultLineMapper<OrderEntity> defaultLineMapper = new DefaultLineMapper<>();
    DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();

    lineTokenizer.setDelimiter(",");
    lineTokenizer.setStrict(false);
    lineTokenizer.setNames("id","destination","deliveryDate","statusOrder","lastUpdated");

    BeanWrapperFieldSetMapper<OrderEntity> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
    fieldSetMapper.setTargetType(OrderEntity.class);

    defaultLineMapper.setLineTokenizer(lineTokenizer);
    defaultLineMapper.setFieldSetMapper(fieldSetMapper);

    return defaultLineMapper;
   }

}

@RestController
@RequestMapping("/loadOrder")
public class OrderLoadController {

@Autowired
JobLauncher jobLauncherOrder;

@Autowired
Job jobOrder;

@GetMapping
public BatchStatus load() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {


    Map<String, JobParameter> maps = new HashMap<>();
    maps.put("time", new JobParameter(System.currentTimeMillis()));
    JobParameters parameters = new JobParameters(maps);
    JobExecution jobExecution = jobLauncherOrder.run(jobOrder, parameters);

    System.out.println("JobExecution: " + jobExecution.getStatus());

    System.out.println("Batch is Running...");
    while (jobExecution.isRunning()) {
        System.out.println("...");
    }

    return jobExecution.getStatus();
    }
}

这也是我的实体类:

@Entity(name = "orders")
@Data
public class OrderEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.EAGER)
private DestinationEntity destination;

private Long deliveryDate;

@Enumerated(value = EnumType.STRING)
private OrderStatus statusOrder;

private Long lastUpdated;

}

这是我的 CSV 文件:

id,destination,deliveryDate,statusOrder,lastUpdated
1,Ploiesti,15-12-2021,NEW,15-12-2021
2,Ploiesti,15-12-2021,NEW,15-12-2021
3,Pitesti,15-12-2021,NEW,15-12-2021
4,Pitesti,15-12-2021,NEW,15-12-2021
5,Pitesti,15-12-2021,NEW,15-12-2021

当我调用端点 localhost:8082/loadController 时,我的数据库没有被填充,它仍然是空的,我得到的只是控制台中的这个错误:

 org.springframework.batch.item.file.FlatFileParseException: Parsing error at line: 2 in resource=[file [C:\Users\ALEX\Desktop\FinalProject\demo\src\main\resources\orders.csv]], input=[1,Ploiesti,15-12-2021,NEW,15-12-2021]
    at org.springframework.batch.item.file.FlatFileItemReader.doRead(FlatFileItemReader.java:189) ~[spring-batch-infrastructure-4.3.4.jar:4.3.4]

 Caused by: org.springframework.validation.BindException: 
org.springframework.validation.BeanPropertyBindingResult: 3 errors
 Field error in object 'target' on field 'lastUpdated': rejected value [15-12-2021]; codes [typeMismatch.target.lastUpdated,typeMismatch.lastUpdated,typeMismatch.java.lang.Long,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.lastUpdated,lastUpdated]; arguments []; default message [lastUpdated]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Long' for property 'lastUpdated'; nested exception is java.lang.NumberFormatException: For input string: "15-12-2021"]
 Field error in object 'target' on field 'destination': rejected value [Ploiesti]; codes [typeMismatch.target.destination,typeMismatch.destination,typeMismatch.com.example.demo.destination.DestinationEntity,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.destination,destination]; arguments []; default message [destination]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'com.example.demo.destination.DestinationEntity' for property 'destination'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.example.demo.destination.DestinationEntity' for property 'destination': no matching editors or conversion strategy found]
 Field error in object 'target' on field 'deliveryDate': rejected value [15-12-2021]; codes [typeMismatch.target.deliveryDate,typeMismatch.deliveryDate,typeMismatch.java.lang.Long,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.deliveryDate,deliveryDate]; arguments []; default message [deliveryDate]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Long' for property 'deliveryDate'; nested exception is java.lang.NumberFormatException: For input string: "15-12-2021"]

最后,我的问题是我应该怎么做,我应该怎么做才能把事情做好? 我更喜欢完整的代码解决方案,因为我不够先进,无法自己解决这个问题,只能通过指示来解决。

【问题讨论】:

  • 从实际阅读错误信息开始。您正在尝试将字符串写入不是字符串的字段
  • @Stultuske 我了解错误消息及其出现的原因,但我不知道我应该怎么做才能使程序正常工作
  • 您希望如何将字符串 'Ploiesti' 转换为 DestinationEntity 的实例 - 该代码在哪里,因为这没有发生并导致您出现此异常。
  • 而且 '15-12-2021' 不是 Long
  • @Chris 我确实有将这些日期转换为长值的代码,但仍然是错误

标签: java spring-boot csv jpa h2


【解决方案1】:

看起来您需要自定义FieldSetMapper,因为您不仅需要将String 转换为Date,然后再转换为Long,还需要通过其名称查找DestinationEntity。这是一个将String 转换为Date 的示例

public class PersonFieldSetMapper implements FieldSetMapper<Person> {

  @Override
  public Person mapFieldSet(FieldSet fieldSet) throws BindException {
    return new Person(fieldSet.readLong("id"),
            fieldSet.readString("firstName"),
            fieldSet.readString("lastName"),
            fieldSet.readDate("birthdate", "yyyy-MM-dd HH:mm:ss"));
  }
}

示例取自这里https://www.dineshonjava.com/spring-batch-read-from-csv-and-write-to-relational-db/

您还需要以某种方式将Map&lt;String, DestinationEntity&gt; 属性添加到此FieldSetMapper 以查找DestinationEntity

【讨论】:

  • 我应该在哪里使用它?
  • lineMapperOrder() 方法中,您需要将BeanWrapperFieldSetMapper&lt;OrderEntity&gt; fieldSetMapper 替换为FieldSetMapper 的自定义实现
  • 这个例子似乎与我的不同,我不知道在不破坏我的情况下如何实现该代码
  • 应该使用这个自定义映射器类而不是我的处理器类,或者我应该在哪里创建它?
  • 你可以在lineMapperOrder()方法中创建它。
【解决方案2】:

您可以使用如下所示的 FieldSetMapper 并从您的代码中替换 Mapper。对每个字段使用基于索引的读取。像id 将是 0 索引


public class OrderEntityMapper implements FieldSetMapper<OrderEntity> {

@Override
public OrderEntity mapFieldSet(FieldSet fieldSet) throws BindException {
    OrderEntity order=new OrderEntity();
    order.setId(fieldSet.readLong(0));
    // other fields
    return order;
}

然后替换BeanWrapperFieldSetMapper&lt;OrderEntity&gt; fieldSetMapper = new BeanWrapperFieldSetMapper&lt;&gt;()like


@Bean
public LineMapper<OrderEntity> lineMapperOrder() {

    DefaultLineMapper<OrderEntity> defaultLineMapper = new DefaultLineMapper<>();
    DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();

    lineTokenizer.setDelimiter(",");
    lineTokenizer.setStrict(false);
    lineTokenizer.setNames("id","destination","deliveryDate","statusOrder","lastUpdated");

      defaultLineMapper.setLineTokenizer(lineTokenizer);
    defaultLineMapper.setFieldSetMapper(new OrderEntityMapper());

    return defaultLineMapper;
   }

}

【讨论】:

  • 你说我应该替换我的 ProcessorOrder 类?
  • 如果我使用它,它会正确映射吗?因为例如在日期的问题似乎是它试图在需要长的地方添加一个字符串
  • 你能测试一下吗?
  • 使用字段来读取 int、String、date、long 等
  • 我不明白它会给我想要的结果,很抱歉,但我认为这超出了我的能力
猜你喜欢
  • 1970-01-01
  • 2015-02-11
  • 2011-02-22
  • 2017-04-17
  • 2020-09-05
  • 2014-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多