以事务方式写入文件

时间:2021-05-01 21:42:29

标签: java spring spring-boot file-io opencsv

我正在尝试写入/追加到 springboot 应用程序中的文件。

我的要求是在过程中发生错误或出现任何问题时回滚写入/追加操作。

我有两种方法:

  1. 使用 openCSV 库写入 csv 文件(按要求正常工作,即事务行为)
public String writeStudentsToCSVFile(List<Student> students, String filePath){

    FileWriter fileWriter = new FileWriter(filePath,true);

    ColumnPositionMappingStrategy columnPositionMappingStrategy = new ColumnPositionMappingStrategy();
    columnPositionMappingStrategy.setType(Student.class);

    String[] columns = new String[] { "id", "firstName", "lastName"};
    columnPositionMappingStrategy.setColumnMapping(columns);

    StatefulBeanToCsvBuilder<Student> beanToCsvBuilder = new StatefulBeanToCsvBuilder(fileWriter).withSeparator(',');
    StatefulBeanToCsv<Student> beanWriter = beanToCsvBuilder.withMappingStrategy(columnPositionMappingStrategy).build();
    
    students.forEach(student -> {
        beanWriter.write(student);
    });

    fileWriter.close();
    return "OK";
}
  1. 使用 BufferedWriter 写入平面文件(TODO ~ 实现事务行为)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public String writeStudentsToFlatFile(List<Student> students, String filePath){

    BufferedWriter bw= Files.newBufferedWriter(Paths.get(filePath));
    
        students.forEach(student -> {
            bw.write(student.toString());
            bw.newLine();
        });

    bw.close();
    return "OK";
}

writeStudentsToCSVFile 以事务方式(以原子方式)工作得非常好,即如果第 10 条记录出现问题,它不会将前 9 条记录中的任何一条写入输出文件。我也想在方法 2 中实现这种类似的行为。

问题在于第二种方法 writeStudentsToFlatFile。我试图通过添加 @Transactional(propagation = Propagation.REQUIRES_NEW) 注释使其具有事务性。但它仍然不起作用。例如,如果第 10 条记录是坏记录,则前 9 条记录仍将写入文件。我的要求是不要将输入列表的任何记录写入文件(即回滚处理的记录直到错误点,即事务行为)

编辑:

我暂时找到了以下解决方法:

我没有单独编写列表中的每个元素,而是通过使用 StringBuilder 并在每个单独元素的末尾附加 lineSeparator 将所有元素相加以使其成为一个巨大的字符串。从而立即将这个构造的 String 写入文件。

我。使用 BufferedWriter:


public String writeStudentsToFlatFile1(List<Student> students, String filePath){

    BufferedWriter bw= Files.newBufferedWriter(Paths.get(filePath));
    
    StringBuilder strb = new StringBuilder();
    
    students.forEach(student -> {
        strb.append(student.toString()).append(System.lineSeparator());
    });

    bw.write(strb.toString());
    bw.close();
    return "OK";
}

二。我也使用 nio FileChannel:

实现了相同的功能
public String writeStudentsToFlatFile2(List<Student> students, String filePath){

    RandomAccessFile file = new RandomAccessFile(filePath, "rw");
    FileChannel channel = file.getChannel();
    
    StringBuilder strb = new StringBuilder();

    for (Student student : students){
            strb.append(student.toString()).append(System.lineSeparator());
        }
        
    ByteBuffer buffer = ByteBuffer.wrap(strb.toString().getBytes(StandardCharsets.UTF_8));
    
    channel.position(channel.size());
    channel.write(buffer);

    channel.close();
    file.close();

    return "OK";
}

要求: 但我正在寻找一种方法来实现这种原子行为,而无需从所有元素中生成一个巨大的字符串。(正如在上面提到的使用开放 CSV 的 writeStudentsToCSVFile 方法中所做的那样)

III.我遇到了来自 Spring BatchTransactionAwareBufferedWriter,并尝试了一个示例实现,但无法使其工作。

public String writeStudentsToFlatFile3(List<Student> list, String filePath){

    RandomAccessFile file = new RandomAccessFile(filePath, "rw");

    FileChannel channel = file.getChannel();
    StringBuilder strb = new StringBuilder();
    TransactionAwareBufferedWriter writer = new TransactionAwareBufferedWriter(channel, null);

    for (Student student : list){
            strb.append(element.toString()).append(System.lineSeparator());
        }

    writer.append(strb.toString());

    writer.close();
    channel.close();
    file.close();

    return "OK";
}

如果在循环中为同一个 writeStudentsToFlatFile3 调用此方法 filePath,它将在第一次迭代后抛出此错误: java.lang.NullPointerException: null at org.springframework.batch.support.transaction.TransactionAwareBufferedWriter.close(TransactionAwareBufferedWriter.java:189) ~[spring-batch-infrastructure-4.2.4.RELEASE.jar:4.2.4.RELEASE]

它可能与使用 Runnable 参数关闭资源有关。

0 个答案:

没有答案