使用 cl_salv_table 显示数据时出现意外的数据库提交
Unexpected database commit when displaying data using cl_salv_table
在 ABAP 程序中,当使用 class cl_salv_table
.
显示本地 table 时,我注意到数据意外持久化
为了重现,我创建了一个最小的代码示例。该程序执行插入,在 ALV 中显示数据,然后执行 ROLLBACK WORK
。
我希望插入的值在回滚之前存在于数据库中,而在回滚之后不存在。
但是,如果在插入和回滚之间显示 ALV 网格,则数据会在回滚之后持久化,并且立即对其他事务可见。
这是预期的行为吗?如果是,我该如何避免?我们确实经常使用这个 class 并且看起来我们可能会在我们实际上不想的时候无意中将值提交给数据库。
这是代码:
*&---------------------------------------------------------------------*
*& Report zok_alv_commit
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*
REPORT zok_alv_commit.
SELECTION-SCREEN BEGIN OF BLOCK b1.
PARAMETERS: p_showtb TYPE boolean AS CHECKBOX DEFAULT abap_false.
SELECTION-SCREEN END OF BLOCK b1.
START-OF-SELECTION.
DATA: lt_table TYPE TABLE OF zok_alv,
ls_table TYPE zok_alv.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Create new GUID and insert into table
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
TRY.
ls_table-guid = cl_system_uuid=>create_uuid_c22_static( ).
CATCH cx_uuid_error.
" Error creating UUID
MESSAGE e836(/basf/dps3_apodata).
ENDTRY.
WRITE: |Create guid { ls_table-guid } |, /.
INSERT zok_alv FROM ls_table.
APPEND ls_table TO lt_table.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" The important bit: show something in an ALV
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
IF p_showtb = abap_true.
cl_salv_table=>factory(
IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = lt_table
).
lo_alv->display( ).
ENDIF.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Check existence in table before and after rollback
" Expectation: If the ALV is shown above, the data is already committed,
" so the ROLLBACK WORK will not have an effect.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
SELECT SINGLE guid FROM zok_alv INTO @DATA(lv_ignored) WHERE guid = @ls_table-guid.
IF sy-subrc = 0.
WRITE: 'GUID exists on DB before rollback.', /.
ELSE.
WRITE: 'GUID does NOT exist on DB before rollback.', /.
ENDIF.
ROLLBACK WORK.
SELECT SINGLE guid FROM zok_alv INTO @lv_ignored WHERE guid = @ls_table-guid.
IF sy-subrc = 0.
WRITE: 'GUID exists on DB after rollback.', /.
ELSE.
WRITE: 'GUID does NOT exist on DB after rollback.', /.
ENDIF.
它需要一个 table ZOK_ALV
,只有 MANDT
和一个 22 个字符的字段 GUID
作为主键,没有别的。
执行带有p_showtb
的代码时未选中:
如您所见,该值在回滚后不存在,并且在 table 中不存在 - 正如预期的那样。
执行带有p_showtb
的代码时勾选:
此时,id 已经在另一个会话的 SE16 中可见:
(我此时离开 ALV 屏幕 Back (F3)
)
代码确认,该值仍然存在,即使在回滚之后也是如此:
即使在离开程序后,这些值仍然存在于数据库中。
如何避免implicit commit
为避免隐式提交,您可以将 INSERT
包装在 UPDATE FUNCTION MODULE 中。
CALL FUNCTION IN UPDATE TASK as SAP LUW,实际插入只会在您调用 COMMIT WORK
.
时发生
你的引用:
However, if between the insert and the rollback, an ALV grid is displayed, the data is persisted beyond the rollback, and immediately visible to other transactions.
我想你的数据库支持Committed Read
Committed read
In committed reads, only the user of a database LUW has access to data modified in the LUW. Other database users cannot access the modified data before a database commit is performed. In the case of reads, a shared lock is set (not possible for the modified data due to the existing exclusive lock). Data can be accessed only when released by a database commit.
但在 SAP LUW 中,你不能指望你可以 select 在 COMMIT WORK 语句之前插入到 UPDATE FUNCTION MODULE 中的插入条目。
我建议你应该始终在内部 table 上工作以进行插入和 ALV 显示,并提供一个 SAVE 按钮来触发从内部 table 到数据库的插入table.
回答 2 个问题:
1) 是的,这是"expected behavior",如database commit documentation :
a database commit is performed implicitly in the following situation: Completion of a dialog step ...
(这意味着任何显示都会提交数据库)
这是因为当屏幕显示时,SAP 除了等待用户操作外不做任何事情,所以在显示之前用于执行 ABAP 代码的 workprocess 可以重新用于执行其他用户请求的ABAP代码。
为了让workprocess可以复用,workprocess(变量)的内存要切换,叫做roll-out/roll-in,这也需要一些为内部 SAP 内容更新系统数据库表,为此需要提交数据库。 SAP LUWs 的文档对此有更好的解释。我在某个地方读到过,但我不记得确切的位置。
2) 不,你不能"avoid this behavior",但考虑到你当前的插入+显示+回滚插入的逻辑,你可以采用以下解决方案之一但我只推荐第一个,不推荐第二个:
- 更改您的逻辑以符合 SAP 规则(即任何显示都会提交数据库,因此请耐心等待)。如果你的逻辑真的是你说的那样,那你为什么要在数据库中插入一些东西然后回滚呢?没有进一步的细节,我的答案是只删除插入和回滚并保留显示。回答其他问题纯粹是猜测,因为您没有提供足够的详细信息来说明您的实际 class 是如何工作的(一定有它插入 + 显示 + 回滚的原因,但是您的解释中缺少什么? ) 你最好post再问一个问题,并提供所有细节。
- 第二种解决方案("non-recommended, counter-performing and dangerous"),如果你真的想坚持你当前的逻辑:将你的显示移动到一个支持 RFC 的功能模块,然后做
CALL FUNCTION '...' DESTINATION 'NONE' KEEPING LOGICAL UNIT OF WORK
(参见 documentation)。不推荐,因为它仅供内部使用。它是反执行的,因为它同时占用了 2 个工作进程。这很危险,因为 "the worst case scenario may be a system shutdown".
在 ABAP 程序中,当使用 class cl_salv_table
.
为了重现,我创建了一个最小的代码示例。该程序执行插入,在 ALV 中显示数据,然后执行 ROLLBACK WORK
。
我希望插入的值在回滚之前存在于数据库中,而在回滚之后不存在。
但是,如果在插入和回滚之间显示 ALV 网格,则数据会在回滚之后持久化,并且立即对其他事务可见。
这是预期的行为吗?如果是,我该如何避免?我们确实经常使用这个 class 并且看起来我们可能会在我们实际上不想的时候无意中将值提交给数据库。
这是代码:
*&---------------------------------------------------------------------*
*& Report zok_alv_commit
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*
REPORT zok_alv_commit.
SELECTION-SCREEN BEGIN OF BLOCK b1.
PARAMETERS: p_showtb TYPE boolean AS CHECKBOX DEFAULT abap_false.
SELECTION-SCREEN END OF BLOCK b1.
START-OF-SELECTION.
DATA: lt_table TYPE TABLE OF zok_alv,
ls_table TYPE zok_alv.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Create new GUID and insert into table
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
TRY.
ls_table-guid = cl_system_uuid=>create_uuid_c22_static( ).
CATCH cx_uuid_error.
" Error creating UUID
MESSAGE e836(/basf/dps3_apodata).
ENDTRY.
WRITE: |Create guid { ls_table-guid } |, /.
INSERT zok_alv FROM ls_table.
APPEND ls_table TO lt_table.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" The important bit: show something in an ALV
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
IF p_showtb = abap_true.
cl_salv_table=>factory(
IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = lt_table
).
lo_alv->display( ).
ENDIF.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Check existence in table before and after rollback
" Expectation: If the ALV is shown above, the data is already committed,
" so the ROLLBACK WORK will not have an effect.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
SELECT SINGLE guid FROM zok_alv INTO @DATA(lv_ignored) WHERE guid = @ls_table-guid.
IF sy-subrc = 0.
WRITE: 'GUID exists on DB before rollback.', /.
ELSE.
WRITE: 'GUID does NOT exist on DB before rollback.', /.
ENDIF.
ROLLBACK WORK.
SELECT SINGLE guid FROM zok_alv INTO @lv_ignored WHERE guid = @ls_table-guid.
IF sy-subrc = 0.
WRITE: 'GUID exists on DB after rollback.', /.
ELSE.
WRITE: 'GUID does NOT exist on DB after rollback.', /.
ENDIF.
它需要一个 table ZOK_ALV
,只有 MANDT
和一个 22 个字符的字段 GUID
作为主键,没有别的。
执行带有p_showtb
的代码时未选中:
如您所见,该值在回滚后不存在,并且在 table 中不存在 - 正如预期的那样。
执行带有p_showtb
的代码时勾选:
此时,id 已经在另一个会话的 SE16 中可见:
(我此时离开 ALV 屏幕 Back (F3)
)
代码确认,该值仍然存在,即使在回滚之后也是如此:
即使在离开程序后,这些值仍然存在于数据库中。
如何避免implicit commit
为避免隐式提交,您可以将 INSERT
包装在 UPDATE FUNCTION MODULE 中。
CALL FUNCTION IN UPDATE TASK as SAP LUW,实际插入只会在您调用 COMMIT WORK
.
你的引用:
However, if between the insert and the rollback, an ALV grid is displayed, the data is persisted beyond the rollback, and immediately visible to other transactions.
我想你的数据库支持Committed Read
Committed read
In committed reads, only the user of a database LUW has access to data modified in the LUW. Other database users cannot access the modified data before a database commit is performed. In the case of reads, a shared lock is set (not possible for the modified data due to the existing exclusive lock). Data can be accessed only when released by a database commit.
但在 SAP LUW 中,你不能指望你可以 select 在 COMMIT WORK 语句之前插入到 UPDATE FUNCTION MODULE 中的插入条目。
我建议你应该始终在内部 table 上工作以进行插入和 ALV 显示,并提供一个 SAVE 按钮来触发从内部 table 到数据库的插入table.
回答 2 个问题:
1) 是的,这是"expected behavior",如database commit documentation :
a database commit is performed implicitly in the following situation: Completion of a dialog step ...
(这意味着任何显示都会提交数据库)
这是因为当屏幕显示时,SAP 除了等待用户操作外不做任何事情,所以在显示之前用于执行 ABAP 代码的 workprocess 可以重新用于执行其他用户请求的ABAP代码。
为了让workprocess可以复用,workprocess(变量)的内存要切换,叫做roll-out/roll-in,这也需要一些为内部 SAP 内容更新系统数据库表,为此需要提交数据库。 SAP LUWs 的文档对此有更好的解释。我在某个地方读到过,但我不记得确切的位置。
2) 不,你不能"avoid this behavior",但考虑到你当前的插入+显示+回滚插入的逻辑,你可以采用以下解决方案之一但我只推荐第一个,不推荐第二个:
- 更改您的逻辑以符合 SAP 规则(即任何显示都会提交数据库,因此请耐心等待)。如果你的逻辑真的是你说的那样,那你为什么要在数据库中插入一些东西然后回滚呢?没有进一步的细节,我的答案是只删除插入和回滚并保留显示。回答其他问题纯粹是猜测,因为您没有提供足够的详细信息来说明您的实际 class 是如何工作的(一定有它插入 + 显示 + 回滚的原因,但是您的解释中缺少什么? ) 你最好post再问一个问题,并提供所有细节。
- 第二种解决方案("non-recommended, counter-performing and dangerous"),如果你真的想坚持你当前的逻辑:将你的显示移动到一个支持 RFC 的功能模块,然后做
CALL FUNCTION '...' DESTINATION 'NONE' KEEPING LOGICAL UNIT OF WORK
(参见 documentation)。不推荐,因为它仅供内部使用。它是反执行的,因为它同时占用了 2 个工作进程。这很危险,因为 "the worst case scenario may be a system shutdown".