我正在尝试写入/追加到 springboot 应用程序中的文件。
我的要求是在过程中发生错误或出现任何问题时回滚写入/追加操作。
我有两种方法:
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";
}
@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 Batch
的 TransactionAwareBufferedWriter,并尝试了一个示例实现,但无法使其工作。
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
参数关闭资源有关。