Lock Objects (Sperrobjekte) gewährleisten die Datenkonsistenz bei gleichzeitigem Zugriff mehrerer Benutzer. Mit ENQUEUE und DEQUEUE werden Sperren gesetzt und freigegeben.
Grundkonzept
| Begriff | Beschreibung |
|---|---|
| Lock Object | Definition in SE11, generiert ENQUEUE/DEQUEUE FuBas |
| ENQUEUE | Sperre setzen |
| DEQUEUE | Sperre freigeben |
| Exclusive Lock (E) | Exklusiver Schreibzugriff |
| Shared Lock (S) | Gemeinsamer Lesezugriff |
| Optimistic Lock (O) | Optimistisch, nur bei Änderung sperren |
Sperrmodi
| Modus | Beschreibung | Kompatibel mit |
|---|---|---|
| E (Exclusive) | Schreibsperre | Keine |
| S (Shared) | Lesesperre | S |
| X (Exclusive, nicht kumulierend) | Strikte Schreibsperre | Keine |
| O (Optimistic) | Optimistisch | S, O |
Beispiele
1. Lock Object erstellen (SE11)
Lock Object: EZ_CUSTOMER
Tabellen:- KNA1 (Primary Table)
Sperrargumente:- MANDT (aus KNA1)- KUNNR (aus KNA1)
Generierte Funktionsbausteine:- ENQUEUE_EZ_CUSTOMER- DEQUEUE_EZ_CUSTOMER2. Einfache Sperre setzen und freigeben
DATA: lv_kunnr TYPE kunnr VALUE '0000001000'.
" Sperre setzenCALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' " Exclusive Lock mandt = sy-mandt kunnr = lv_kunnr EXCEPTIONS foreign_lock = 1 " Bereits von anderem gesperrt system_failure = 2 OTHERS = 3.
IF sy-subrc = 0. " Sperre erfolgreich - Daten bearbeiten UPDATE kna1 SET name1 = 'Neuer Name' WHERE kunnr = lv_kunnr.
COMMIT WORK.
" Sperre freigeben CALL FUNCTION 'DEQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' mandt = sy-mandt kunnr = lv_kunnr.
ELSEIF sy-subrc = 1. " Von anderem Benutzer gesperrt MESSAGE |Kunde { lv_kunnr } ist gesperrt von { sy-msgv1 }| TYPE 'E'.ENDIF.3. Sperrklasse für saubere Handhabung
CLASS zcl_customer_lock DEFINITION. PUBLIC SECTION. METHODS: lock IMPORTING iv_kunnr TYPE kunnr RETURNING VALUE(rv_success) TYPE abap_bool.
METHODS: unlock IMPORTING iv_kunnr TYPE kunnr.
METHODS: unlock_all.
METHODS: is_locked IMPORTING iv_kunnr TYPE kunnr RETURNING VALUE(rv_locked) TYPE abap_bool.
METHODS: get_lock_owner IMPORTING iv_kunnr TYPE kunnr RETURNING VALUE(rv_user) TYPE sy-uname.
PRIVATE SECTION. DATA: mt_locked_customers TYPE TABLE OF kunnr.ENDCLASS.
CLASS zcl_customer_lock IMPLEMENTATION. METHOD lock. CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' mandt = sy-mandt kunnr = iv_kunnr _wait = abap_true " Warten wenn gesperrt _collect = abap_false EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.
IF sy-subrc = 0. rv_success = abap_true. APPEND iv_kunnr TO mt_locked_customers. ELSE. rv_success = abap_false. ENDIF. ENDMETHOD.
METHOD unlock. CALL FUNCTION 'DEQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' mandt = sy-mandt kunnr = iv_kunnr.
DELETE mt_locked_customers WHERE table_line = iv_kunnr. ENDMETHOD.
METHOD unlock_all. LOOP AT mt_locked_customers INTO DATA(lv_kunnr). unlock( lv_kunnr ). ENDLOOP. ENDMETHOD.
METHOD is_locked. CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' mandt = sy-mandt kunnr = iv_kunnr _wait = abap_false EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.
IF sy-subrc = 1. rv_locked = abap_true. ELSE. " Sperre wieder freigeben (war nur Test) CALL FUNCTION 'DEQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' mandt = sy-mandt kunnr = iv_kunnr. rv_locked = abap_false. ENDIF. ENDMETHOD.
METHOD get_lock_owner. DATA: lt_locks TYPE TABLE OF seqg3.
" Alle Sperren lesen CALL FUNCTION 'ENQUEUE_READ' EXPORTING gname = 'KNA1' garg = |{ sy-mandt }{ iv_kunnr }| TABLES enq = lt_locks EXCEPTIONS communication_failure = 1 system_failure = 2 OTHERS = 3.
IF sy-subrc = 0 AND lt_locks IS NOT INITIAL. rv_user = lt_locks[ 1 ]-guname. ENDIF. ENDMETHOD.ENDCLASS.
" VerwendungDATA(lo_lock) = NEW zcl_customer_lock( ).
IF lo_lock->lock( '0000001000' ). " Bearbeitung...
lo_lock->unlock( '0000001000' ).ELSE. DATA(lv_owner) = lo_lock->get_lock_owner( '0000001000' ). MESSAGE |Gesperrt von { lv_owner }| TYPE 'E'.ENDIF.4. Warten auf Sperre
" Mit _WAIT = abap_true wartet der Aufruf bis zu 10 SekundenCALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' kunnr = lv_kunnr _wait = abap_true " Warten aktivieren EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.
" Oder: Eigene Warteschleife mit TimeoutDATA: lv_attempts TYPE i VALUE 0, lv_max_attempts TYPE i VALUE 10.
WHILE lv_attempts < lv_max_attempts. CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING kunnr = lv_kunnr _wait = abap_false EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc = 0. EXIT. " Sperre erhalten ENDIF.
lv_attempts = lv_attempts + 1. WAIT UP TO 1 SECONDS.ENDWHILE.
IF sy-subrc <> 0. MESSAGE 'Sperre konnte nicht erhalten werden' TYPE 'E'.ENDIF.5. Kollektive Sperren (_COLLECT)
" Sperren sammeln statt sofort setzenDATA: lt_customers TYPE TABLE OF kunnr.
lt_customers = VALUE #( ( '0000001000' ) ( '0000001001' ) ( '0000001002' ) ).
" Sperren sammelnLOOP AT lt_customers INTO DATA(lv_kunnr). CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING kunnr = lv_kunnr _collect = abap_true " Sammeln EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc <> 0. " Fehlerbehandlung ENDIF.ENDLOOP.
" Alle gesammelten Sperren auf einmal setzenCALL FUNCTION 'FLUSH_ENQUEUE' EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.
IF sy-subrc <> 0. " Mindestens eine Sperre konnte nicht gesetzt werden " Alle bereits gesetzten werden zurückgenommenENDIF.6. Shared Lock für Lesezugriff
" Shared Lock - mehrere Leser gleichzeitig möglichCALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'S' " Shared Lock kunnr = lv_kunnr EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.
IF sy-subrc = 0. " Lesen (andere können auch lesen) SELECT SINGLE * FROM kna1 WHERE kunnr = @lv_kunnr INTO @DATA(ls_customer).
CALL FUNCTION 'DEQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'S' kunnr = lv_kunnr.ENDIF.7. Optimistic Locking
CLASS zcl_optimistic_lock DEFINITION. PUBLIC SECTION. METHODS: read_for_update IMPORTING iv_kunnr TYPE kunnr EXPORTING es_customer TYPE kna1 ev_timestamp TYPE timestampl.
METHODS: save_changes IMPORTING is_customer TYPE kna1 iv_timestamp TYPE timestampl RETURNING VALUE(rv_success) TYPE abap_bool.ENDCLASS.
CLASS zcl_optimistic_lock IMPLEMENTATION. METHOD read_for_update. " Optimistic Lock setzen CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'O' " Optimistic kunnr = iv_kunnr EXCEPTIONS OTHERS = 1.
" Daten lesen SELECT SINGLE * FROM kna1 WHERE kunnr = @iv_kunnr INTO @es_customer.
GET TIME STAMP FIELD ev_timestamp. ENDMETHOD.
METHOD save_changes. " Vor dem Speichern: Exklusive Sperre versuchen CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' kunnr = is_customer-kunnr _convert = abap_true " O -> E konvertieren EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc = 0. " Prüfen ob Daten geändert wurden SELECT SINGLE aedat aezet FROM kna1 WHERE kunnr = @is_customer-kunnr INTO @DATA(ls_check).
" Wenn zwischenzeitlich geändert -> Konflikt " (Vereinfachte Prüfung)
UPDATE kna1 FROM @is_customer. rv_success = xsdbool( sy-subrc = 0 ).
CALL FUNCTION 'DEQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' kunnr = is_customer-kunnr. ELSE. rv_success = abap_false. ENDIF. ENDMETHOD.ENDCLASS.8. Alle Sperren eines Objekttyps lesen
DATA: lt_locks TYPE TABLE OF seqg3.
" Alle Sperren für Tabelle KNA1 lesenCALL FUNCTION 'ENQUEUE_READ' EXPORTING gclient = sy-mandt gname = 'KNA1' " Tabellenname guname = '*' " Alle Benutzer TABLES enq = lt_locks EXCEPTIONS communication_failure = 1 system_failure = 2 OTHERS = 3.
IF sy-subrc = 0. LOOP AT lt_locks INTO DATA(ls_lock). WRITE: / 'Objekt:', ls_lock-garg, / 'Benutzer:', ls_lock-guname, / 'Modus:', ls_lock-gmode. ENDLOOP.ENDIF.9. DEQUEUE_ALL - Alle eigenen Sperren freigeben
" Alle vom aktuellen Programm gesetzten Sperren freigebenCALL FUNCTION 'DEQUEUE_ALL'.
" Alternativ: Spezifisches Lock ObjectCALL FUNCTION 'DEQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' kunnr = space. " Leer = Alle Sperren dieses Objekts10. Lock Object mit mehreren Tabellen
Lock Object: EZ_ORDER (SE11)
Tabellen:- VBAK (Primary Table - Kopf)- VBAP (Secondary Table - Positionen)
Sperrargumente:- MANDT- VBELN (Gemeinsamer Schlüssel)" Eine Sperre sperrt Kopf und PositionenCALL FUNCTION 'ENQUEUE_EZ_ORDER' EXPORTING mode_vbak = 'E' mode_vbap = 'E' vbeln = lv_vbeln EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc = 0. " Kopf und Positionen bearbeiten UPDATE vbak SET ... WHERE vbeln = lv_vbeln. UPDATE vbap SET ... WHERE vbeln = lv_vbeln.
COMMIT WORK.
CALL FUNCTION 'DEQUEUE_EZ_ORDER' EXPORTING mode_vbak = 'E' mode_vbap = 'E' vbeln = lv_vbeln.ENDIF.11. Sperre mit Scope
" _SCOPE Parameter bestimmt Lebensdauer" 1 = Dialog (Standard) - Sperre bei COMMIT freigegeben" 2 = Update Task - Sperre wird an Update Task übergeben" 3 = Beide
CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' kunnr = lv_kunnr _scope = '2' " Für Update Task EXCEPTIONS OTHERS = 1.
" Änderung in Update TaskCALL FUNCTION 'Z_UPDATE_CUSTOMER' IN UPDATE TASK EXPORTING is_customer = ls_customer.
COMMIT WORK." Sperre wird nach Update Task automatisch freigegeben12. Deadlock-Vermeidung
CLASS zcl_deadlock_safe DEFINITION. PUBLIC SECTION. METHODS: lock_multiple IMPORTING it_keys TYPE ty_key_tab RETURNING VALUE(rv_success) TYPE abap_bool.
PRIVATE SECTION. METHODS: sort_keys CHANGING ct_keys TYPE ty_key_tab.ENDCLASS.
CLASS zcl_deadlock_safe IMPLEMENTATION. METHOD lock_multiple. DATA: lt_keys TYPE ty_key_tab.
lt_keys = it_keys.
" WICHTIG: Immer in gleicher Reihenfolge sperren " verhindert Deadlocks sort_keys( CHANGING ct_keys = lt_keys ).
" Sperren setzen LOOP AT lt_keys INTO DATA(ls_key). CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING kunnr = ls_key-kunnr _wait = abap_false EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc <> 0. " Rollback: Bereits gesetzte Sperren freigeben DATA(lv_index) = sy-tabix - 1. LOOP AT lt_keys INTO DATA(ls_unlock) TO lv_index. CALL FUNCTION 'DEQUEUE_EZ_CUSTOMER' EXPORTING kunnr = ls_unlock-kunnr. ENDLOOP.
rv_success = abap_false. RETURN. ENDIF. ENDLOOP.
rv_success = abap_true. ENDMETHOD.
METHOD sort_keys. SORT ct_keys BY kunnr. ENDMETHOD.ENDCLASS.13. RAP: ETag und Optimistic Concurrency
" In RAP wird Concurrency über ETags gesteuert" Behavior Definition:define behavior for ZI_Customer with etag master last_changed_at lock master{ update; delete;}
" Implementierung für Custom LockCLASS lhc_customer DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS lock FOR LOCK IMPORTING keys FOR LOCK Customer.ENDCLASS.
CLASS lhc_customer IMPLEMENTATION. METHOD lock. LOOP AT keys INTO DATA(ls_key). CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING kunnr = ls_key-kunnr EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc <> 0. APPEND VALUE #( %tky = ls_key-%tky ) TO failed-customer.
APPEND VALUE #( %tky = ls_key-%tky %msg = NEW zcm_customer( severity = if_abap_behv_message=>severity-error textid = zcm_customer=>locked ) ) TO reported-customer. ENDIF. ENDLOOP. ENDMETHOD.ENDCLASS.14. Transaktion SM12 - Sperreinträge anzeigen
" Programmatisch Sperren anzeigen (wie SM12)DATA: lt_locks TYPE TABLE OF seqg3.
CALL FUNCTION 'ENQUEUE_READ' EXPORTING guname = sy-uname " Nur eigene Sperren TABLES enq = lt_locks EXCEPTIONS OTHERS = 1.
" ReportWRITE: / 'Aktive Sperren:'.WRITE: / '---------------'.
LOOP AT lt_locks INTO DATA(ls_lock). WRITE: / 'Tabelle:', ls_lock-gname, / 'Argument:', ls_lock-garg, / 'Modus:', ls_lock-gmode, / 'Benutzer:', ls_lock-guname, / 'Zeit:', ls_lock-gttime. SKIP.ENDLOOP.15. Cleanup bei Programmabbruch
CLASS zcl_lock_manager DEFINITION. PUBLIC SECTION. METHODS: constructor. METHODS: destructor. " Wird bei Garbage Collection aufgerufen
METHODS: lock_customer IMPORTING iv_kunnr TYPE kunnr.
PRIVATE SECTION. DATA: mt_locks TYPE TABLE OF kunnr.ENDCLASS.
CLASS zcl_lock_manager IMPLEMENTATION. METHOD constructor. " Optional: Registrierung für Cleanup ENDMETHOD.
METHOD destructor. " Sperren freigeben wenn Objekt zerstört wird LOOP AT mt_locks INTO DATA(lv_kunnr). CALL FUNCTION 'DEQUEUE_EZ_CUSTOMER' EXPORTING kunnr = lv_kunnr. ENDLOOP. ENDMETHOD.
METHOD lock_customer. CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING kunnr = iv_kunnr EXCEPTIONS OTHERS = 1.
IF sy-subrc = 0. APPEND iv_kunnr TO mt_locks. ENDIF. ENDMETHOD.ENDCLASS.
" Verwendung mit TRY-CLEANUPTRY. DATA(lo_lock) = NEW zcl_lock_manager( ). lo_lock->lock_customer( '1000' ).
" Verarbeitung...
CLEANUP. " Wird auch bei Exceptions ausgeführt IF lo_lock IS BOUND. CLEAR lo_lock. " Triggert Destruktor ENDIF.ENDTRY.Lock Object in SE11 erstellen
- SE11 → Lock Object → Name:
EZ_<Name> - Primärtabelle angeben
- Sperrargumente definieren (Schlüsselfelder)
- Aktivieren → generiert ENQUEUE/DEQUEUE FuBas
Wichtige Hinweise / Best Practice
- Immer DEQUEUE aufrufen – auch im Fehlerfall.
- Sperren kurz halten – nur während der Änderung.
- Gleiche Reihenfolge bei mehreren Sperren (Deadlock-Vermeidung).
- _WAIT = abap_true für automatisches Warten.
- _COLLECT für atomares Setzen mehrerer Sperren.
- Optimistic Locking für lange Bearbeitungszeiten.
- SM12 für Analyse aktiver Sperren.
- COMMIT WORK gibt Sperren frei (Scope 1).
- Lock Objects mit EZ_ Namenskonvention benennen.
- Kombinieren Sie mit Exception Classes für Fehlerbehandlung.