8

我最近开始使用 Spring 的数据源事务管理器。我现在有个问题。我的事务包括对数据库表的更新和对文件的写入操作。

它工作正常,但我对文件 I/O 有一些疑问。正如您在下面看到的,我已将我的 bean 的 openFile 和 closeFile 方法分别配置为 init-method 和 destroy-method,这反过来又提供了像构造函数和析构函数一样调用这些方法。如果文件没有正确关闭,一些记录可能没有成功写入 output.txt 文件,这意味着我也无法正确处理事务管理。

但是,我想回滚那些尚未附加到平面文件的数据库更新。使用我的解决方案,似乎不可能将 fileClose 方法添加到事务中。有谁知道如何正确实施这个期望的行动?

任何建议将不胜感激

<!--XML CONFIGURATION -->
<bean id="myFileWriter" class="com.job.step.ItemFileWriter"  init-method="openFile" destroy-method="closeFile">
    <property name="jdbcTemplate" ref="jdbcTemplateProduct"/>   
</bean> 

public class ItemFileWriter implements ItemWriter<Item> {
private static final Logger log = Logger.getLogger(ItemFileWriter.class);   
private BufferedWriter bw = null;
public void openFile() throws IOException {
    try {
        bw = new BufferedWriter(new FileWriter("C:\\output.txt"));
    } catch (IOException e) {           
        //log.error(e);
        throw e;
    }       
}
public void closeFile() throws IOException {
    if (bw != null) {
        try {
            bw.close();
        } catch (IOException e) {
            log.error(e);
            throw e;
        }
    }
}

@Transactional(rollbackFor = IOException.class)
public void write(List<? extends Item> itemList) throws IOException 
{               
    for (Iterator<? extends Item> iterator = itemList.iterator(); iterator.hasNext();) {
        Item item = (Item) iterator.next();

        String updateRtlnOutbound = "UPDATE SAMPLESCHEMA.SAMPLETABLE SET STATUS='TRANSFERRED' WHERE ID = ?";
        jdbcTemplate.update(updateRtlnOutbound, new Object[]{item.getID()});

        String item = String.format("%09d\n", item.customerNumber);
        bw.write(item);
    }                           
}
}   
4

2 回答 2

4

一般来说,文件 IO 不是事务性的(除了一些特定于操作系统的功能)。

因此,您可以做的最好的是将打开和关闭操作移至write()方法,以便在事务中执行它们并在关闭失败时回滚事务。

但是请注意,在事务回滚的情况下,您不能回滚文件 IO,因此在某些情况下您可以获得正确的文件与项目,而在数据库中这些项目没有标记为TRANSFERRED.

为了解决这个问题,你可以尝试使用低级事务管理支持,并在回滚的情况下尝试删除文件,但我认为它仍然不能提供一致性的强保证:

@Transactional(rollbackFor = IOException.class)
public void write(List<? extends Item> itemList) throws IOException 
{                
    openFile();
    TransactionSynchronizationManager().registerSynchronization(new TransactionSynchronizationAdapter() {
        public void afterCompletion(int status) {
            if (status = STATUS_ROLLED_BACK) {
                // try to delete the file
            }
        }
    });

    try {
        ...
    } finally {
        closeFile();                        
    }
}
于 2011-03-09T12:43:46.593 回答
3

您正在对两个不同的系统进行操作:文件系统和数据库。通常,XA 事务使我们可以轻松地将不同的事务系统组合成一个事务。

大多数数据库都可以参与 XA 事务。对于文件系统,您可以使用XADisk来启用 XA。一旦在数据库(通过数据源的正确配置)和文件系统(通过 xdisk)上启用 XA,您可以确保文件和数据库操作都提交或两者都回滚。

于 2012-02-10T19:10:45.197 回答