Los Lock Objects (objetos de bloqueo) garantizan la consistencia de datos cuando múltiples usuarios acceden simultáneamente. Con ENQUEUE y DEQUEUE se establecen y liberan bloqueos.
Concepto básico
| Término | Descripción |
|---|---|
| Lock Object | Definición en SE11, genera funciones ENQUEUE/DEQUEUE |
| ENQUEUE | Establecer bloqueo |
| DEQUEUE | Liberar bloqueo |
| Exclusive Lock (E) | Acceso de escritura exclusivo |
| Shared Lock (S) | Acceso de lectura compartido |
| Optimistic Lock (O) | Optimista, bloquear solo al modificar |
Modos de bloqueo
| Modo | Descripción | Compatible con |
|---|---|---|
| E (Exclusive) | Bloqueo de escritura | Ninguno |
| S (Shared) | Bloqueo de lectura | S |
| X (Exclusive, no acumulativo) | Bloqueo de escritura estricto | Ninguno |
| O (Optimistic) | Optimista | S, O |
Ejemplos
1. Crear Lock Object (SE11)
Lock Object: EZ_CUSTOMER
Tablas:- KNA1 (Primary Table)
Argumentos de bloqueo:- MANDT (de KNA1)- KUNNR (de KNA1)
Funciones generadas:- ENQUEUE_EZ_CUSTOMER- DEQUEUE_EZ_CUSTOMER2. Establecer y liberar bloqueo simple
DATA: lv_kunnr TYPE kunnr VALUE '0000001000'.
" Establecer bloqueoCALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' " Exclusive Lock mandt = sy-mandt kunnr = lv_kunnr EXCEPTIONS foreign_lock = 1 " Ya bloqueado por otro system_failure = 2 OTHERS = 3.
IF sy-subrc = 0. " Bloqueo exitoso - editar datos UPDATE kna1 SET name1 = 'Nuevo nombre' WHERE kunnr = lv_kunnr.
COMMIT WORK.
" Liberar bloqueo CALL FUNCTION 'DEQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' mandt = sy-mandt kunnr = lv_kunnr.
ELSEIF sy-subrc = 1. " Bloqueado por otro usuario MESSAGE |Cliente { lv_kunnr } está bloqueado por { sy-msgv1 }| TYPE 'E'.ENDIF.3. Clase de bloqueo para manejo limpio
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 " Esperar si está bloqueado _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. " Liberar bloqueo (era solo prueba) 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.
" Leer todos los bloqueos 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.
" UsoDATA(lo_lock) = NEW zcl_customer_lock( ).
IF lo_lock->lock( '0000001000' ). " Edición...
lo_lock->unlock( '0000001000' ).ELSE. DATA(lv_owner) = lo_lock->get_lock_owner( '0000001000' ). MESSAGE |Bloqueado por { lv_owner }| TYPE 'E'.ENDIF.4. Esperar por bloqueo
" Con _WAIT = abap_true la llamada espera hasta 10 segundosCALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' kunnr = lv_kunnr _wait = abap_true " Activar espera EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.
" O: Bucle de espera propio con 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. " Bloqueo obtenido ENDIF.
lv_attempts = lv_attempts + 1. WAIT UP TO 1 SECONDS.ENDWHILE.
IF sy-subrc <> 0. MESSAGE 'No se pudo obtener el bloqueo' TYPE 'E'.ENDIF.5. Bloqueos colectivos (_COLLECT)
" Recopilar bloqueos en lugar de establecerlos inmediatamenteDATA: lt_customers TYPE TABLE OF kunnr.
lt_customers = VALUE #( ( '0000001000' ) ( '0000001001' ) ( '0000001002' ) ).
" Recopilar bloqueosLOOP AT lt_customers INTO DATA(lv_kunnr). CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING kunnr = lv_kunnr _collect = abap_true " Recopilar EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc <> 0. " Manejo de errores ENDIF.ENDLOOP.
" Establecer todos los bloqueos recopilados de una vezCALL FUNCTION 'FLUSH_ENQUEUE' EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.
IF sy-subrc <> 0. " Al menos un bloqueo no se pudo establecer " Todos los ya establecidos se revocanENDIF.6. Shared Lock para acceso de lectura
" Shared Lock - múltiples lectores simultáneos posiblesCALL 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. " Leer (otros también pueden leer) 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. Bloqueo optimista
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. " Establecer bloqueo optimista CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'O' " Optimistic kunnr = iv_kunnr EXCEPTIONS OTHERS = 1.
" Leer datos SELECT SINGLE * FROM kna1 WHERE kunnr = @iv_kunnr INTO @es_customer.
GET TIME STAMP FIELD ev_timestamp. ENDMETHOD.
METHOD save_changes. " Antes de guardar: Intentar bloqueo exclusivo 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. " Verificar si los datos fueron modificados SELECT SINGLE aedat aezet FROM kna1 WHERE kunnr = @is_customer-kunnr INTO @DATA(ls_check).
" Si se modificó entretanto -> Conflicto " (Verificación simplificada)
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. Leer todos los bloqueos de un tipo de objeto
DATA: lt_locks TYPE TABLE OF seqg3.
" Leer todos los bloqueos para tabla KNA1CALL FUNCTION 'ENQUEUE_READ' EXPORTING gclient = sy-mandt gname = 'KNA1' " Nombre de tabla guname = '*' " Todos los usuarios 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: / 'Objeto:', ls_lock-garg, / 'Usuario:', ls_lock-guname, / 'Modo:', ls_lock-gmode. ENDLOOP.ENDIF.9. DEQUEUE_ALL - Liberar todos los bloqueos propios
" Liberar todos los bloqueos establecidos por el programa actualCALL FUNCTION 'DEQUEUE_ALL'.
" Alternativa: Lock Object específicoCALL FUNCTION 'DEQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' kunnr = space. " Vacío = Todos los bloqueos de este objeto10. Lock Object con múltiples tablas
Lock Object: EZ_ORDER (SE11)
Tablas:- VBAK (Primary Table - Cabecera)- VBAP (Secondary Table - Posiciones)
Argumentos de bloqueo:- MANDT- VBELN (Clave común)" Un bloqueo bloquea cabecera y posicionesCALL FUNCTION 'ENQUEUE_EZ_ORDER' EXPORTING mode_vbak = 'E' mode_vbap = 'E' vbeln = lv_vbeln EXCEPTIONS foreign_lock = 1 OTHERS = 2.
IF sy-subrc = 0. " Editar cabecera y posiciones 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. Bloqueo con Scope
" El parámetro _SCOPE determina la duración" 1 = Dialog (Estándar) - Bloqueo liberado en COMMIT" 2 = Update Task - Bloqueo se pasa a Update Task" 3 = Ambos
CALL FUNCTION 'ENQUEUE_EZ_CUSTOMER' EXPORTING mode_kna1 = 'E' kunnr = lv_kunnr _scope = '2' " Para Update Task EXCEPTIONS OTHERS = 1.
" Modificación en Update TaskCALL FUNCTION 'Z_UPDATE_CUSTOMER' IN UPDATE TASK EXPORTING is_customer = ls_customer.
COMMIT WORK." El bloqueo se libera automáticamente después de Update Task12. Evitar 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.
" IMPORTANTE: Siempre bloquear en el mismo orden " evita deadlocks sort_keys( CHANGING ct_keys = lt_keys ).
" Establecer bloqueos 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: Liberar bloqueos ya establecidos 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 y Optimistic Concurrency
" En RAP la concurrencia se controla mediante ETags" Behavior Definition:define behavior for ZI_Customer with etag master last_changed_at lock master{ update; delete;}
" Implementación para 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. Transacción SM12 - Mostrar entradas de bloqueo
" Mostrar bloqueos programáticamente (como SM12)DATA: lt_locks TYPE TABLE OF seqg3.
CALL FUNCTION 'ENQUEUE_READ' EXPORTING guname = sy-uname " Solo bloqueos propios TABLES enq = lt_locks EXCEPTIONS OTHERS = 1.
" ReportWRITE: / 'Bloqueos activos:'.WRITE: / '---------------'.
LOOP AT lt_locks INTO DATA(ls_lock). WRITE: / 'Tabla:', ls_lock-gname, / 'Argumento:', ls_lock-garg, / 'Modo:', ls_lock-gmode, / 'Usuario:', ls_lock-guname, / 'Hora:', ls_lock-gttime. SKIP.ENDLOOP.15. Limpieza en caso de terminación del programa
CLASS zcl_lock_manager DEFINITION. PUBLIC SECTION. METHODS: constructor. METHODS: destructor. " Se llama en 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. " Opcional: Registro para limpieza ENDMETHOD.
METHOD destructor. " Liberar bloqueos cuando el objeto es destruido 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.
" Uso con TRY-CLEANUPTRY. DATA(lo_lock) = NEW zcl_lock_manager( ). lo_lock->lock_customer( '1000' ).
" Procesamiento...
CLEANUP. " Se ejecuta automáticamente en Exceptions IF lo_lock IS BOUND. CLEAR lo_lock. " Dispara destructor ENDIF.ENDTRY.Crear Lock Object en SE11
- SE11 → Lock Object → Nombre:
EZ_<Nombre> - Indicar tabla primaria
- Definir argumentos de bloqueo (campos clave)
- Activar → genera funciones ENQUEUE/DEQUEUE
Notas importantes / Mejores prácticas
- Siempre llamar DEQUEUE – también en caso de error.
- Mantener bloqueos cortos – solo durante la modificación.
- Mismo orden para múltiples bloqueos (evitar deadlocks).
- _WAIT = abap_true para espera automática.
- _COLLECT para establecer múltiples bloqueos atómicamente.
- Bloqueo optimista para tiempos de edición largos.
- SM12 para análisis de bloqueos activos.
- COMMIT WORK libera bloqueos (Scope 1).
- Nombrar Lock Objects con convención EZ_.
- Combinar con Exception Classes para manejo de errores.