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

Popular posts from this blog

New syntax for append- VALUE (new syntax 7.4+) in ABAP

Read statement new syntax in ABAP. (7.4+).

Concatenation new syntax( 7.4+ ).