当关闭文件方法抛出 IOException 时如何管理事务(包括文件 IO)

发布于 2024-10-21 04:16:34 字数 1810 浏览 4 评论 0原文

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

它工作正常,但我对文件 I/O 有一些疑问。如下所示,我已将 bean 的 openFile 和 closeFile 方法分别配置为 init 方法和 destroy 方法,这反过来又提供了这些方法,就像构造函数和析构函数一样被调用。如果文件没有正确关闭,某些记录可能无法成功写入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);
    }                           
}
}   

I've recently begun using Spring's Data Source Transaction Manager. I have a problem now.
My transaction includes updates to a DB table and a write operation to a file.

It works fine but I have some doubts about file I/O. As you see below, I have configured openFile and closeFile methods of my bean as the init-method and the destroy-method respectively, which in turn provides those methods to be called just like a constuructor and a destructor. If the file is not closed properly, some of the records may not have successfully been written to the output.txt file which means that I have not been able to handle transaction management properly as well.

However, I'd like to rollback those DB updates which have not been appended to the flat file. With my solution, it looks impossible to add the fileClose method to the transaction. Does anyone know how to implement this desired action properly?

Any suggestions would be greatly appreciated

<!--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);
    }                           
}
}   

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

烈酒灼喉 2024-10-28 04:16:34

一般来说,文件 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();                        
    }
}

Generally speaking, file IO is not transactional (except for some OS-specific features).

So, the best you can do is to move open and close operation to write() method, in order to execute them inside a transaction and rollback the transaction if closing fails.

Note, however, that you can't rollback the file IO in the case of transaction rollback, so that under some circumstances you can get the correct file with items, whereas in the database these items are not marked as TRANSFERRED.

To sove this problem you can try to use low-level transaction management support and try to delete the file in the case of rollback, but I think it still can't provide strong guarantees of consistency:

@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();                        
    }
}
沉睡月亮 2024-10-28 04:16:34

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

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

You are doing operations over two different systems: file-system and database. Typically, XA transactions make it easy for us to combine different transactional systems into a single transaction.

Most databases can be made to participate in XA transactions. For file-system you can use XADisk to enable XA. Once you enable XA on both database (through right configuration of data-source) and file-system (through xadisk), you can be sure that both the file and database operations commit or both of them rollback.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文