【问题标题】:ORA-01002: fetch out of sequence while using @transactionalORA-01002: 使用 @transactional 时获取乱序
【发布时间】:2025-11-28 02:15:01
【问题描述】:

当我尝试从我的应用程序调用一个过程时得到一个错误提示 ORA-01002: fetch out of sequence

使用的技术:

  • Mybatis 3
  • Spring MVC

这里有趣的一点是,只有当我对服务类中的调用方法使用 @Transactional (org.springframework.transaction.annotation.Transactional) 注释时才会发生错误。如果我删除 @Transactional 则没有 ORA 错误。

我正在使用@Transactional,因为我有几个 DAO 注入到一个服务中。请在下面找到我粘贴的代码。

@Transactional
    public boolean saveavgFlyHrs(AverageFlyingHoursReport averageFlyingHoursReport) throws TransactionDataException {
        String status = null;
        boolean isOk = false;
        if(averageFlyingHoursReportDAO.saveavgFlyHrs(averageFlyingHoursReport)) {
            status = averageFlyingHoursReportDAO.updateCheckEff(averageFlyingHoursReport.getSubFleet());
            logger.debug("OUT_STATUS:"+status);
            if(ConstantStringUtil.SUCCESS.equalsIgnoreCase(status)) {
                isOk = true;
            } else {
                isOk = false;
            }
        }
        return isOk;
    }

请任何人帮助我。

【问题讨论】:

    标签: spring oracle spring-mvc plsql mybatis


    【解决方案1】:

    ORA-01002 是 Oracle 错误。您没有显示任何 Oracle 代码,所以我们必须猜测会发生什么。

    当您通过FOR UPDATE 光标提交时通常会抛出此错误,例如:

    SQL> CREATE TABLE TEST (ID NUMBER, c VARCHAR2(10));
    
    Table created
    
    SQL> INSERT INTO TEST VALUES (1, 'a');
    
    1 row inserted
    
    SQL> INSERT INTO TEST VALUES (2, 'b');
    
    1 row inserted
    
    SQL> BEGIN
      2     FOR cc IN (SELECT * FROM TEST FOR UPDATE) LOOP -- FOR UPDATE cursor
      3        UPDATE TEST SET c = UPPER(c) WHERE ID = cc.id;
      4        COMMIT; -- this will invalidate our cursor
      5     END LOOP;
      6  END;
      7  /
    
    ORA-01002: fetch out of sequence
    ORA-06512: at line 3
    

    我可以想象,将@Transactional 添加到工作单元将使其成功提交/错误回滚。所以也许这段代码是使用某种FOR UPDATE 游标的更大循环的一部分。当您添加@Transactional 时,它会在每次调用该方法时提交,从而使主游标无效。

    如果您在回滚一些会使游标无效的更改后尝试从游标中获取,也可能会遇到ORA-01002

    SQL> DECLARE
      2     CURSOR cc IS SELECT * FROM TEST;
      3     rc cc%ROWTYPE;
      4  BEGIN
      5     UPDATE TEST SET c = 'c' WHERE ID = 2;
      6     OPEN cc;
      7     FETCH cc INTO rc;
      8     -- do other things
      9     ROLLBACK;
     10     FETCH cc INTO rc;
     11  END;
     12  /
    
    ORA-01002: fetch out of sequence
    ORA-06512: at line 11
    

    这里我们的游标cc 无效,因为我们已经回滚了一些影响游标中行的更改。同样,这可能是由于添加了@Transactional 和回滚事务的方法,而另一个游标仍在从中获取。

    结论:您应该只将@Transactional 添加到执行不可分割工作量的单元中。如果该方法是更大事务的子方法,则不应自行提交/回滚。

    【讨论】:

    • 嗨文森特,太好了!你是绝对正确的..感谢那些有用的解释。在您解释的场景 1 下的每次更新后,我已经在我的程序中提交了提交。你建议从程序中删除提交还是删除@Transactional?
    • @prabu 理想情况下,只有主程序应该决定何时提交,因为只有它知道什么构成事务。我不确定我是否理解@Transactional 的所有微妙之处,但我认为小方法和子方法不应该有这个属性,因为它们不是独立的事务。遵循相同的逻辑,如果您有 Oracle 过程,它们不应该自己提交,它们应该让客户端(这里是调用应用程序)决定何时/是否提交。
    • 谢谢文森特!!将按照您的建议实施,因为这个想法看起来很有希望..