Les Lock Objects (objets de verrouillage) garantissent la cohérence des données en cas d’accès simultané par plusieurs utilisateurs. Avec ENQUEUE et DEQUEUE, les verrous sont posés et libérés.
Concept de base
| Terme | Description |
|---|---|
| Lock Object | Définition dans SE11, génère les modules fonction ENQUEUE/DEQUEUE |
| ENQUEUE | Poser un verrou |
| DEQUEUE | Libérer un verrou |
| Exclusive Lock (E) | Accès exclusif en écriture |
| Shared Lock (S) | Accès partagé en lecture |
| Optimistic Lock (O) | Optimiste, verrouiller uniquement lors de la modification |
Modes de verrouillage
| Mode | Description | Compatible avec |
|---|---|---|
| E (Exclusive) | Verrou en écriture | Aucun |
| S (Shared) | Verrou en lecture | S |
| X (Exclusive, non cumulatif) | Verrou strict en écriture | Aucun |
| O (Optimistic) | Optimiste | S, O |
Exemples
1. Créer un Lock Object (SE11)
Lock Object: EZ_CUSTOMER
Tables:- KNA1 (Primary Table)
Arguments de verrouillage:- MANDT (de KNA1)- KUNNR (de KNA1)
Modules fonction générés:- ENQUEUE_EZ_CUSTOMER- DEQUEUE_EZ_CUSTOMER2. Poser et libérer un verrou simple
DATA: lv_kunnr TYPE kunnr VALUE '0000001000'.
" Poser le verrouCALL FUNCTION 'ENQUEUE_EZ_CUSTOMER" EXPORTING mode_kna1 = 'E' " Exclusive Lock mandt = sy-mandt kunnr = lv_kunnr EXCEPTIONS foreign_lock = 1 " Déjà verrouillé par un autre system_failure = 2 OTHERS = 3.
IF sy-subrc = 0. " Verrou réussi - traiter les données UPDATE kna1 SET name1 = 'Nouveau nom" WHERE kunnr = lv_kunnr.
COMMIT WORK.
" Libérer le verrou CALL FUNCTION 'DEQUEUE_EZ_CUSTOMER" EXPORTING mode_kna1 = 'E" mandt = sy-mandt kunnr = lv_kunnr.
ELSEIF sy-subrc = 1. " Verrouillé par un autre utilisateur MESSAGE |Client { lv_kunnr } est verrouillé par { sy-msgv1 }| TYPE 'E'.ENDIF.3. Classe de verrouillage pour une gestion propre
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 " Attendre si verrouillé _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. " Libérer à nouveau le verrou (c'était juste un 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.
" Lire tous les verrous 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.
" UtilisationDATA(lo_lock) = NEW zcl_customer_lock( ).
IF lo_lock->lock( '0000001000' ). " Traitement...
lo_lock->unlock( '0000001000' ).ELSE. DATA(lv_owner) = lo_lock->get_lock_owner( '0000001000' ). MESSAGE |Verrouillé par { lv_owner }| TYPE 'E'.ENDIF.4. Attendre un verrou
" Avec _WAIT = abap_true, l'appel attend jusqu'à 10 secondesCALL FUNCTION 'ENQUEUE_EZ_CUSTOMER" EXPORTING mode_kna1 = 'E" kunnr = lv_kunnr _wait = abap_true " Activer l'attente EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.
" Ou : Boucle d'attente personnalisée avec 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. " Verrou obtenu ENDIF.
lv_attempts = lv_attempts + 1. WAIT UP TO 1 SECONDS.ENDWHILE.
IF sy-subrc <> 0. MESSAGE 'Le verrou n''a pas pu être obtenu' TYPE 'E'.ENDIF.5. Verrous collectifs (_COLLECT)
" Collecter les verrous au lieu de les poser immédiatementDATA: lt_customers TYPE TABLE OF kunnr.
lt_customers = VALUE #( ( '0000001000' ) ( '0000001001' ) ( '0000001002' ) ).
" Collecter les verrousLOOP AT lt_customers INTO DATA(lv_kunnr). CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER" EXPORTING kunnr = lv_kunnr _collect = abap_true " Collecter EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc <> 0. " Gestion des erreurs ENDIF.ENDLOOP.
" Poser tous les verrous collectés en une seule foisCALL FUNCTION 'FLUSH_ENQUEUE" EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.
IF sy-subrc <> 0. " Au moins un verrou n'a pas pu être posé " Tous ceux déjà posés sont annulésENDIF.6. Shared Lock pour accès en lecture
" Shared Lock - plusieurs lecteurs possibles simultanémentCALL 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. " Lecture (d'autres peuvent aussi lire) 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. " Poser un Optimistic Lock CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER" EXPORTING mode_kna1 = 'O' " Optimistic kunnr = iv_kunnr EXCEPTIONS OTHERS = 1.
" Lire les données SELECT SINGLE * FROM kna1 WHERE kunnr = @iv_kunnr INTO @es_customer.
GET TIME STAMP FIELD ev_timestamp. ENDMETHOD.
METHOD save_changes. " Avant de sauvegarder : Tenter un verrou exclusif CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER" EXPORTING mode_kna1 = 'E" kunnr = is_customer-kunnr _convert = abap_true " Convertir O -> E EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc = 0. " Vérifier si les données ont été modifiées SELECT SINGLE aedat aezet FROM kna1 WHERE kunnr = @is_customer-kunnr INTO @DATA(ls_check).
" Si modifié entre-temps -> Conflit " (Vérification simplifiée)
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. Lire tous les verrous d’un type d’objet
DATA: lt_locks TYPE TABLE OF seqg3.
" Lire tous les verrous pour la table KNA1CALL FUNCTION 'ENQUEUE_READ" EXPORTING gclient = sy-mandt gname = 'KNA1' " Nom de table guname = '*' " Tous les utilisateurs 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: / 'Objet:', ls_lock-garg, / 'Utilisateur:', ls_lock-guname, / 'Mode:', ls_lock-gmode. ENDLOOP.ENDIF.9. DEQUEUE_ALL - Libérer tous ses propres verrous
" Libérer tous les verrous posés par le programme actuelCALL FUNCTION 'DEQUEUE_ALL'.
" Alternative : Lock Object spécifiqueCALL FUNCTION 'DEQUEUE_EZ_CUSTOMER" EXPORTING mode_kna1 = 'E" kunnr = space. " Vide = Tous les verrous de cet objet10. Lock Object avec plusieurs tables
Lock Object: EZ_ORDER (SE11)
Tables:- VBAK (Primary Table - En-tête)- VBAP (Secondary Table - Postes)
Arguments de verrouillage:- MANDT- VBELN (Clé commune)" Un verrou verrouille l'en-tête et les postesCALL FUNCTION 'ENQUEUE_EZ_ORDER" EXPORTING mode_vbak = 'E" mode_vbap = 'E" vbeln = lv_vbeln EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc = 0. " Traiter l'en-tête et les postes 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. Verrou avec Scope
" Le paramètre _SCOPE détermine la durée de vie" 1 = Dialog (Standard) - Verrou libéré lors de COMMIT" 2 = Update Task - Verrou transmis à l'Update Task" 3 = Les deux
CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER" EXPORTING mode_kna1 = 'E" kunnr = lv_kunnr _scope = '2' " Pour Update Task EXCEPTIONS OTHERS = 1.
" Modification dans Update TaskCALL FUNCTION 'Z_UPDATE_CUSTOMER' IN UPDATE TASK EXPORTING is_customer = ls_customer.
COMMIT WORK." Le verrou est automatiquement libéré après Update Task12. Éviter les deadlocks
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.
" IMPORTANT : Toujours verrouiller dans le même ordre " évite les deadlocks sort_keys( CHANGING ct_keys = lt_keys ).
" Poser les verrous 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 : Libérer les verrous déjà posés 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 et Optimistic Concurrency
" Dans RAP, la concurrence est gérée via des ETags" Behavior Definition :define behavior for ZI_Customer with etag master last_changed_at lock master{ update; delete;}
" Implémentation pour 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. Transaction SM12 - Afficher les entrées de verrouillage
" Afficher les verrous programmatiquement (comme SM12)DATA: lt_locks TYPE TABLE OF seqg3.
CALL FUNCTION 'ENQUEUE_READ" EXPORTING guname = sy-uname " Uniquement ses propres verrous TABLES enq = lt_locks EXCEPTIONS OTHERS = 1.
" ReportWRITE: / 'Verrous actifs :'.WRITE: / '---------------'.
LOOP AT lt_locks INTO DATA(ls_lock). WRITE: / 'Table:', ls_lock-gname, / 'Argument:', ls_lock-garg, / 'Mode:', ls_lock-gmode, / 'Utilisateur:', ls_lock-guname, / 'Heure:', ls_lock-gttime. SKIP.ENDLOOP.15. Nettoyage en cas d’interruption du programme
CLASS zcl_lock_manager DEFINITION. PUBLIC SECTION. METHODS: constructor. METHODS: destructor. " Appelé lors du Garbage Collection
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. " Optionnel : Enregistrement pour le cleanup ENDMETHOD.
METHOD destructor. " Libérer les verrous lorsque l'objet est détruit 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.
" Utilisation avec TRY-CLEANUPTRY. DATA(lo_lock) = NEW zcl_lock_manager( ). lo_lock->lock_customer( '1000' ).
" Traitement...
CLEANUP. " Exécuté également en cas d'exceptions IF lo_lock IS BOUND. CLEAR lo_lock. " Déclenche le destructeur ENDIF.ENDTRY.Créer un Lock Object dans SE11
- SE11 → Lock Object → Nom :
EZ_<Nom> - Indiquer la table primaire
- Définir les arguments de verrouillage (champs clés)
- Activer → génère les modules fonction ENQUEUE/DEQUEUE
Remarques importantes / Best Practice
- Toujours appeler DEQUEUE - même en cas d’erreur.
- Maintenir les verrous courts - uniquement pendant la modification.
- Même ordre pour plusieurs verrous (éviter les deadlocks).
- _WAIT = abap_true pour attente automatique.
- _COLLECT pour pose atomique de plusieurs verrous.
- Optimistic Locking pour de longues durées de traitement.
- SM12 pour analyser les verrous actifs.
- COMMIT WORK libère les verrous (Scope 1).
- Nommer les Lock Objects avec la convention EZ_.
- Combinez avec Exception Classes pour la gestion des erreurs.