Etag in RAP Unmanaged
In RAP, ETag
is used for optimistic concurrency control.
-
It prevents data overwrite when two users attempt to update the same record.
-
It relies on the
If-Match
HTTP header sent by the UI (e.g. Fiori Elements). -
The backend compares the current DB version with the ETag (like
last_changed_at
) sent by the client. -
If they don't match, raise a concurrency error.
Refer:- Lock object in Unmanaged scenario in RAP
Step 1: Check the fields in table level (Already maintained- Header).
* In this blog I used last_ch_date as Created date and last_ch_time as Last changed date.
Step 2: Add the fields in interface view entity- like below (Header).
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Interface entity view for student header'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define root view entity ZIV_AB_STUH_13
as select from ztl_ab_stuh_13
composition [ 0..*] of ZIV_AB_STUI_13 as _Result
association to ZD_AB_STU_GENDER as _Gender on $projection.StuGender = _Gender.Value
{
key id as Id,
stu_id as StuId,
stu_name as StuName,
stu_age as StuAge,
stu_course as StuCourse,
stu_cour_dur as StuCourDur,
stu_status as StuStatus,
stu_gender as StuGender,
stu_gen_desc as StuGenDesc,
stu_dob as StuDob,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
last_ch_time as LastChTime,
last_ch_date as LastChDate,
_Result,
_Gender
}
Step 3: Check the consumption view- (Header- Used that fields).
Step 4: Change the behavior def-Interface view Header.
unmanaged implementation in class zbp_iv_ab_stuh_13 unique;
strict ( 2 );
with draft;
define behavior for ZIV_AB_STUH_13 alias Student
draft table ZDRA_AB_STUH_13
early numbering
lock master
total etag LastChTime
authorization master ( instance )
etag master LastChTime
{
create ( authorization : global );
update ;
delete ;
field ( readonly ) Id,StuId, StuGenDesc;
association _Result { create;with draft; }
draft action Edit;
draft action Activate optimized;
draft action Discard;
draft action Resume;
draft determine action Prepare;
mapping for ztl_ab_stuh_13 control zstr_up_stuh
{
Id = id;
StuId = stu_id;
StuName = stu_name;
StuAge = stu_age;
StuCourse = stu_course;
StuCourDur = stu_cour_dur;
StuGender = stu_gender;
StuGenDesc = stu_gen_desc;
StuDob = stu_dob;
StuStatus = stu_status;
LastChDate = last_ch_date;
LastChTime = last_ch_time;
}
}
define behavior for ZIV_AB_STUI_13 alias Result
draft table ZDRA_AB_STUI_13
early numbering
lock dependent by _Student
authorization dependent by _Student
etag dependent by _Student
{
update;
delete;
field ( readonly ) Id, CourseDesc, SemesterDesc, SemReDesc;
association _Student{ with draft; }
mapping for ztl_ab_stui_13
{
Id = id;
Course = course;
CourseDesc = course_desc;
Semester = semester;
SemesterDesc = semester_desc;
SemResult = sem_result;
SemReDesc = sem_re_desc;
}
}
Step 5: Change the behavior def-consumption view Header.
projection;
strict ( 2 );
use draft;
define behavior for ZCV_AB_STUH_13 alias Student
use etag
{
use create;
use update;
use delete;
use action Edit;
use action Activate;
use action Discard;
use action Resume;
use action Prepare;
use association _Result { create; with draft; }
}
define behavior for ZCV_AB_STUI_13 alias Result
{
use update;
use delete;
use association _Student { with draft; }
}
Step 6: Change the custom implementation class (Create and update methods).
CLASS zcl_ab_stuh_13 DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
TYPES:
tt_create_student TYPE TABLE FOR CREATE ziv_ab_stuh_13,
tt_map_early TYPE RESPONSE FOR MAPPED EARLY ziv_ab_stuh_13,
tt_failed_early TYPE RESPONSE FOR FAILED EARLY ziv_ab_stuh_13,
tt_report_early TYPE RESPONSE FOR REPORTED EARLY ziv_ab_stuh_13,
tt_response_late TYPE RESPONSE FOR REPORTED LATE ziv_ab_stuh_13,
tt_keys_student TYPE TABLE FOR READ IMPORT ziv_ab_stuh_13, "\\student,
tt_result_student TYPE TABLE FOR READ RESULT ziv_ab_stuh_13, "\\student,
tt_update_student TYPE TABLE FOR UPDATE ziv_ab_stuh_13, "\\student,
tt_delete_student TYPE TABLE FOR DELETE ziv_ab_stuh_13,
tt_cba_results TYPE TABLE FOR CREATE ziv_ab_stuh_13\\student\_result.
CLASS-METHODS: get_instance
RETURNING VALUE(r_instance)
TYPE REF TO zcl_ab_stuh_13,
create_student
IMPORTING
entities TYPE tt_create_student
CHANGING
mapped TYPE tt_map_early
failed TYPE tt_failed_early
reported TYPE tt_report_early,
get_next_id
RETURNING VALUE(rv_id) TYPE ziv_ab_stuh_13-StuId,
earlynumbering_create
IMPORTING entities TYPE tt_create_student
CHANGING mapped TYPE tt_map_early
failed TYPE tt_failed_early
reported TYPE tt_report_early
RAISING
cx_uuid_error,
save_data
CHANGING
reported TYPE tt_response_late,
read_student
IMPORTING
keys TYPE tt_keys_student
CHANGING
result TYPE tt_result_student
failed TYPE tt_failed_early
reported TYPE tt_report_early,
update
IMPORTING
entities TYPE tt_update_student
CHANGING
mapped TYPE tt_map_early
failed TYPE tt_failed_early
reported TYPE tt_report_early,
delete
IMPORTING
keys TYPE tt_delete_student
CHANGING
mapped TYPE tt_map_early
failed TYPE tt_failed_early
reported TYPE tt_report_early,
earlynumbering_cba_result
IMPORTING
entities TYPE tt_cba_results
CHANGING
mapped TYPE tt_map_early
failed TYPE tt_failed_early
reported TYPE tt_report_early,
cba_result_create
IMPORTING
entities_cba TYPE tt_cba_results
CHANGING
mapped TYPE tt_map_early
failed TYPE tt_failed_early
reported TYPE tt_report_early.
PROTECTED SECTION.
PRIVATE SECTION.
CLASS-DATA: mo_instance TYPE REF TO zcl_ab_stuh_13,
gt_student TYPE STANDARD TABLE OF ztl_ab_stuh_13, " WITH EMPTY KEY,
gs_student TYPE ztl_ab_stuh_13,
gs_mmaped TYPE tt_map_early,
gr_student_d TYPE RANGE OF ztl_ab_stuh_13-id,
gt_results TYPE STANDARD TABLE OF ztl_ab_stui_13,
lv_date type timestampl.
ENDCLASS.
CLASS zcl_ab_stuh_13 IMPLEMENTATION.
METHOD get_instance.
mo_instance = r_instance = COND #( WHEN mo_instance IS BOUND
THEN mo_instance
ELSE NEW #( ) ).
ENDMETHOD.
METHOD get_next_id.
SELECT MAX( stu_id ) FROM ztl_ab_stuh_13 INTO @DATA(lv_id1).
rv_id = lv_id1 + 1.
ENDMETHOD.
METHOD earlynumbering_create.
DATA(ls_mapped) = gs_mmaped.
DATA(lv_id) = cl_uuid_factory=>create_system_uuid( )->create_uuid_x16( ).
DATA(lv_stuid) = get_next_id( ).
READ TABLE gt_student ASSIGNING FIELD-SYMBOL(<gs_student>) INDEX 1.
IF <gs_student> IS ASSIGNED.
<gs_student>-id = lv_stuid.
UNASSIGN <gs_student>.
ENDIF.
mapped-student = VALUE #(
FOR ls_entities IN entities WHERE ( id IS INITIAL )
(
%cid = ls_entities-%cid
%is_draft = ls_entities-%is_draft
Id = lv_id ) ) .
ENDMETHOD.
METHOD create_student.
LOOP AT entities ASSIGNING FIELD-SYMBOL(<ls_entities>).
gt_student = CORRESPONDING #( entities MAPPING FROM ENTITY ).
IF gt_student IS NOT INITIAL.
gt_student[ 1 ]-stu_id = get_next_id( ).
get TIME STAMP FIELD lv_date.
gt_student[ 1 ]-last_ch_date = lv_date.
gt_student[ 1 ]-last_ch_time = lv_date.
mapped-student = VALUE #( (
%cid = <ls_entities>-%cid
%key = <ls_entities>-%key
%is_draft = <ls_entities>-%is_draft
) ) .
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD save_data.
IF NOT gt_student[] IS INITIAL.
MODIFY ztl_ab_stuh_13 FROM TABLE @gt_student.
ENDIF.
IF NOT gr_student_d IS INITIAL.
DELETE FROM ztl_ab_stuh_13 WHERE id IN @gr_student_d.
ENDIF.
IF NOT gt_results IS INITIAL.
MODIFY ztl_ab_stui_13 FROM TABLE @gt_results.
ENDIF.
ENDMETHOD.
METHOD read_student.
SELECT * FROM ztl_ab_stuh_13
FOR ALL ENTRIES IN @keys
WHERE id = @keys-Id
INTO TABLE @DATA(lt_read_student).
result = CORRESPONDING #( lt_read_student MAPPING TO ENTITY ).
ENDMETHOD.
METHOD update.
DATA: lt_stu_update TYPE STANDARD TABLE OF ztl_ab_stuh_13,
lt_stu_update_x TYPE STANDARD TABLE OF zstr_up_stuh.
lt_stu_update = CORRESPONDING #( entities MAPPING FROM ENTITY ).
lt_stu_update_x = CORRESPONDING #( entities MAPPING FROM ENTITY USING CONTROL ).
IF NOT lt_stu_update IS INITIAL.
SELECT * FROM ztl_ab_stuh_13
FOR ALL ENTRIES IN @lt_stu_update
WHERE id = @lt_stu_update-Id
INTO TABLE @DATA(lt_stu_old).
ENDIF.
get TIME STAMP FIELD lv_date.
gt_student = VALUE #(
"FOR idx=1 idx <= lines( lt_stu_update )
LET
ls_control_flag = VALUE #( lt_stu_update_x[ 1 ] OPTIONAL )
ls_student_new = VALUE #( lt_stu_update[ 1 ] OPTIONAL )
ls_student_old = VALUE #( lt_stu_old[ id = ls_student_new-id ] OPTIONAL )
IN
(
id = ls_student_new-id
stu_id = COND #( WHEN ls_control_flag-stu_id IS NOT INITIAL
THEN ls_student_new-stu_id
ELSE ls_student_old-stu_id )
stu_name = COND #( WHEN ls_control_flag-stu_name IS NOT INITIAL
THEN ls_student_new-stu_name
ELSE ls_student_old-stu_name )
stu_age = COND #( WHEN ls_control_flag-stu_age IS NOT INITIAL
THEN ls_student_new-stu_age
ELSE ls_student_old-stu_age )
stu_course = COND #( WHEN ls_control_flag-stu_course IS NOT INITIAL
THEN ls_student_new-stu_course
ELSE ls_student_old-stu_course )
stu_cour_dur = COND #( WHEN ls_control_flag-stu_cour_dur IS NOT INITIAL
THEN ls_student_new-stu_cour_dur
ELSE ls_student_old-stu_cour_dur )
stu_status = COND #( WHEN ls_control_flag-stu_status IS NOT INITIAL
THEN ls_student_new-stu_status
ELSE ls_student_old-stu_status )
stu_gender = COND #( WHEN ls_control_flag-stu_gender IS NOT INITIAL
THEN ls_student_new-stu_gender
ELSE ls_student_old-stu_gender )
stu_gen_desc = COND #( WHEN ls_control_flag-stu_gen_desc IS NOT INITIAL
THEN ls_student_new-stu_gen_desc
ELSE ls_student_old-stu_gen_desc )
stu_dob = COND #( WHEN ls_control_flag-stu_dob IS NOT INITIAL
THEN ls_student_new-stu_dob
ELSE ls_student_old-stu_dob )
last_ch_time = lv_date
last_ch_date = ls_student_old-last_ch_date"lv_date
) ).
ENDMETHOD.
METHOD delete.
DATA: lt_stu_delete TYPE TABLE OF ztl_ab_stuh_13.
lt_stu_delete = CORRESPONDING #( keys MAPPING FROM ENTITY ).
gr_student_d = VALUE #(
FOR ls_student IN lt_stu_delete
sign = 'I'
option = 'EQ'
( low = ls_student-id )
).
ENDMETHOD.
METHOD earlynumbering_cba_result.
LOOP AT entities ASSIGNING FIELD-SYMBOL(<ls_entities>).
LOOP AT <ls_entities>-%target ASSIGNING FIELD-SYMBOL(<ls_results>).
mapped-result = VALUE #( (
%cid = <ls_results>-%cid
%is_draft = <ls_results>-%is_draft
%key = <ls_results>-%key ) ) .
ENDLOOP.
ENDLOOP..
ENDMETHOD.
METHOD cba_result_create.
gt_results = VALUE #(
FOR ls_entities IN entities_cba
FOR ls_result IN ls_entities-%target
LET
ls_rap_results = CORRESPONDING ztl_ab_stui_13( ls_result MAPPING FROM ENTITY )
IN
(
ls_rap_results
)
).
mapped = VALUE #(
result = VALUE #(
FOR x = 1 WHILE x <= lines( entities_cba )
LET
lt_results = VALUE #( entities_cba[ x ]-%target OPTIONAL )
IN
FOR y = 1 WHILE y <= lines( lt_results )
LET
ls_curr_res = VALUE #( lt_results[ y ] OPTIONAL )
IN
(
%cid = ls_curr_res-%cid
%key = ls_curr_res-%key
Id = ls_curr_res-Id
)
) ).
ENDMETHOD.
ENDCLASS.
Testing:
* Preview the application.
* Click on go to see all records.
* Select one record and navigate to object page.
* I used same application in 2 tabs.
* I edited one record in first tab in object page and saved.
* I tried to edit the same record in second tab in object page throw error like below.
******************************Thank you*******************************
Comments
Post a Comment