【问题标题】:Spring @Transactional rollbackFor not working for checked Exception for method called outside proxy objectSpring @Transactional rollbackFor 不适用于调用外部代理对象的方法的检查异常
【发布时间】:2015-02-06 02:39:26
【问题描述】:

我在结合 Spring 回滚 Hibernate 更新时遇到问题。

我有以下课程:

@Service
@Transactional(readOnly = true)
public class DocumentServiceImpl extends AbstractGenericService<Document, IDocumentDao> implements DocumentService {

    @Override
    @Transactional(readOnly=false, rollbackFor = DocumentServiceException.class)
    public void saveDocument(final DocumentForm form, final BindingResult result, final CustomUserContext userContext) throws DocumentServiceException {
        Document document = locateDocument(form, userContext);
        if (!result.hasErrors()) {
            try {
                updateDocumentCategories(form, document);
                storeDocument(document, form.getDocumentId(), form.getFile());
                solrService.addDocument(document);
            } catch (IOException e) {
                result.reject("error.uploading.file");
                throw new DocumentServiceException("Error trying to copy the uploaded file to its final destination", e);
            } catch (SolrServerException e) {
                result.reject("error.uploading.file.solr");
                throw new DocumentServiceException("Solr had an error parsing your uploaded file", e);
            }
        }
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = IOException.class)
    public void storeDocument(Document document, String documentId, CommonsMultipartFile uploadedFile) throws IOException {
        getDao().saveOrUpdate(document);
        if (StringUtils.isBlank(documentId)) {
            File newFile = documentLocator.createFile(document);
            uploadedFile.transferTo(newFile);
            // Todo: TEST FOR ROLLBACK ON FILE I/O EXCEPTION
            throw new IOException("this is a test");
        }
    }

该接口未使用任何@Transactional 注释进行标记。 saveDocument() 方法是直接从我的 Controller 调用的,所以我希望使用该方法的 @Transactional 配置,尤其是 rollbackFor 参数。但是,当抛出 DocumentServiceException 时,不会回滚任何内容(即 getDao().saveOrUpdate(document) 被持久化)。出于测试目的,我在 storeDocument 方法中添加了一个“抛出新的 IOException”。希望有人能帮助我解决这个问题,不胜感激。

【问题讨论】:

  • @Transactional 上的 @Transactional 注释是无用的,因为它是一个内部方法调用,只有调用代理才会应用 AOP。如果没有回滚,我怀疑您的设置有问题,实际上没有应用任何事务。另一件事是有点奇怪,恕我直言,让你的BindingResult 旁边将它绑定到网络(你直接使用与网络相关的CommonsMultipartFile)。基本上你的服务层现在依赖于网络......
  • 启用 spring 日志以验证事务是否开始
  • @"M. Deinum".. storeDocument 方法也被其他控制器单独调用,因此我们需要@Transactional 注解。我知道如果间接调用它是没有用的。
  • @Andy 事务有效。我通过抛出 RuntimeException(而不是 IOException)进行了另一项测试,在这种情况下,它回滚了 saveOrUpdate 提交。

标签: java spring hibernate transactions annotations


【解决方案1】:
  1. @Transactional 注释放置正确。您不必在接口级别设置它,因为它不会自动继承(如果您的具体类实现了两个具有冲突事务设置的接口)。

  2. 当你说方法被直接调用时,我假设你的接口是@Autowired 而不是具体的实现。

  3. 在您的服务方法中放置一个断点并检查您的堆栈跟踪中是否有 TransactionInterceptor 条目。如果你没有,那么你的事务管理配置是错误的,你根本没有使用 Spring 事务管理。

丹尼斯更新

  1. 还有一件事也许可以帮助其他人:

    我的 applicationContext 中有 tx:annotation-driven。 applicationContext 包含对所有 bean 的组件扫描(无过滤器)。

    但是,dispatcherServlet 上下文还包含对所有 bean 的组件扫描(遗留代码,不要射击信使)。所以基本上我有一份我所有的豆子的副本,因为它们在两种情况下都被扫描过。

    并且因为在 dispatcherServlet 上下文中创建的 bean 不包含 tx:annotation-driven 元素,所以 dispatcherServlet 上下文中的服务 bean 不是事务性的。

  2. 我不得不将 dispatcherServlet 上下文中的组件扫描更改为:

    <context:component-scan base-package="your/base/package" use-default-filters="false">    
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 
    </context:component-scan> 
    

    因此,它只会实例化调度程序 servlet 上下文中的控制器(并且没有自动装配的依赖项,例如它的服务),以及 applicationContext 中的服务/daos。

来自 applicationContext 的服务然后被事务化。

【讨论】:

  • 该死,你对第 3 点是正确的。我没有看到 TransactionInterceptor。我猜其他东西(容器?)在检测到 RuntimeException 时正在回滚更改?
  • 如果没有 TransactionInterceptor 你只是在使用自动提交,所以每个语句都在它自己的事务中。因此,如果您有 5 个语句和 4 个工作,而最后一个失败,您仍然会看到前 4 个语句保持不变,而只有最后一个回滚。
  • 不确定。如果我在使用 Hibernate 保存记录后抛出 RuntimeException,记录会回滚。所以事务范围必须大于您的建议?
  • 也依赖于db连接管理。如果所有查询都看到相同的连接并且没有自动提交,那么您实际上有一个事务。我认为它需要调试。
  • 还有一件事可能对其他人有所帮助:我的 applicationContext 中有 tx:annotation-driven。 applicationContext 包含对所有 bean 的组件扫描(无过滤器)。但是,dispatcherServlet 上下文还包含对所有 bean 的组件扫描(遗留代码,不要射击信使)。所以基本上我有一份我所有的豆子的副本,因为它们在两种情况下都被扫描过。而且因为在 dispatcherServlet 上下文中创建的 bean 不包含 tx:annotation-driven 元素,所以 dispatcherServlet 上下文中的服务 bean 不是事务性的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-18
  • 2020-10-25
  • 2012-11-25
  • 2023-03-31
  • 1970-01-01
  • 2018-10-13
  • 2018-08-10
相关资源
最近更新 更多