【问题标题】:Spring+Hibernate: Inserting mutliple records and @TransactionalSpring+Hibernate:插入多条记录和@Transactional
【发布时间】:2016-01-23 11:26:23
【问题描述】:

我有一个基于 Spring+Hibernate 构建的应用程序。我正在尝试从文件中的条目列表将数据插入数据库。基本思想是插入所有具有有效数据的记录,并为所有未插入的条目记录错误报告。

一个服务类,即@Transactional,调用另一个服务类来读取文件并插入到数据库中。此刻,我一个接一个地插入记录。也许我会将其更改为批处理。插入在 try catch 块中执行。

伪代码:

@Transactional
class MainService {     
     public void insertFromFile(File inputFile) {
          subServuce(toIterator(inputFile)); // toIterator(..) is just for understanding. 
     } 
}

class SubService {
    public void insert(Iterator input) {
        while (input.hasNext()) {
             try {
                 DBService.save(input.next());
             } catch (Exception e) {
                 // Log the error and continue with next records
             }
        }
    }
}

@Transactional
class DBService {
     public void save(Data input) {
          generalDao.save(input);
     }
}

试过了:

class SubService {
    public void insert(Iterator input) {
        while (input.hasNext()) {
             try {
                 save(input.next());
             } catch (Exception e) {
                 // Log the error and continue with next records
             }
        }
    }

    // @Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = Exception.class)
    @Transactional(propagation = Propagation.NOT_SUPPORTED, noRollbackFor = Exception.class)
    public void save(Data input) {
        DBService.save(input);
    }
}

即使处理了异常,整个事务也在回滚,我收到此错误Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly

我尝试在SubService.insert(..) 上指定@Transactional(noRollBack = Exception.class),但没有成功。

以下是我的问题:

  1. 如何告诉 Spring 不要回滚并继续处理其他记录。
  2. 在插入多条记录时不回滚是一种好习惯吗?这不违反原子性规则吗?
  3. 在批处理中,这是如何处理的?如果发生错误是全部插入还是不插入?

    解决方案:

在@Madhusudana Reddy Sunnapu 的回答的帮助下,能够解决这个问题。

使用 Propagation.REQUIRES_NEW 在 SubService 和 DBService 之间创建了一个层。问题是即使 SubService.save(..) 使用 @Transactional 进行注释,因为它属于同一个 Bean,代理不会将其视为不同的会话。

SubServiceDao {
    // @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save(Data input) {
        DBService.save(input);
    }
}

class SubService {
    public void insert(Iterator input) {
        while (input.hasNext()) {
             try {
                 SubServiceDao.save(input.next());
             } catch (Exception e) {
                 // Log the error and continue with next records
             }
        }
    }
}

【问题讨论】:

  • stackoverflow.com/questions/54343137/…

标签: java spring hibernate jpa transactional


【解决方案1】:

尽管您发现了异常,但 Spring 仍然能够检测到 entityManager 出现问题,在这种情况下,它会将事务标记为仅回滚。

这是因为 entityManager Spring 注入是一个代理,它会在委托给真正的 entityManager 之前拦截所有调用,并让它知道真正的 entityManager 已抛出异常,即使您捕获它也是如此。

我看到的一个选项是您可以在 DBService.save(...) 方法 @Transactional(propagation=Propagation.REQUIRES_NEW) 上启动新事务。这样,如果由于无效数据而发生任何异常,它只会影响该行的插入,其余的仍将继续。

【讨论】:

  • 嘿! DBService 本身使用@Transactional 进行注释。我更新了显示 DBService 类的问题。无论如何,我将调用 DBService 的部分移动到另一个方法并将其标记为 Propagation.REQUIRES_NEW/NOT_SUPPORTED。没有工作。
  • 您是否尝试在 dao 类的 generalDao.save(input) 方法上添加 Propagation.REQUIRES_NEW。因为您应该在与entityManager 交互的方法上添加此注释。我相信generalDao.save(input) 是与实体管理器交互的那个。剩余的其他人可以在同一个事务中运行。
  • generalDao 这里是code.google.com/p/hibernate-generic-dao,我无法为我的特定目的更改它的实现。所以我在这里无能为力。
  • 知道了。尽管您在SubService 类中使用REQUIRES_NEW 注释了save(input) 方法,但我看到的问题是,您是在while 循环中直接调用此save 方法。如果我们直接调用一个方法,它将不是 Spring AOP 代理(没有新事务),因此它将与调用者在同一个事务中运行。作为一种快速解决方法,您可以使用 - ((SubService) AopContext.currentProxy()).save(input) 调用 save 方法,或者更好地将 save(input) 方法移动到新 bean 中并使用这个新 bean 调用它。
  • 抱歉回复晚了。您给出的初步答案帮助我解决了问题。我在 SubService 和 DBService 之间添加了另一个 DAO,并将 persist 方法移至该类。我用解决方案更新了我的答案。
【解决方案2】:

来自Sessionjavadoc:

如果 Session 抛出异常,事务必须滚动 返回并丢弃会话。 Session的内部状态 异常发生后可能与数据库不一致。

因此,整个事务被回滚并且没有插入任何内容。

在插入之前进行验证(如果需要,还需要进行必要的锁定),并在失败的情况下重试整个事务。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-26
    相关资源
    最近更新 更多