Introduction
In the evolving landscape of SAP development, the ABAP RESTful Application Programming Model (RAP) has emerged as a pivotal framework for building modern, cloud-ready applications. While RAP offers both managed and unmanaged scenarios, the unmanaged approach provides developers with greater flexibility and control over data persistence and business logic.
One of the complex operations in application development is the deep insert—creating a parent entity along with its associated child entities in a single transactional call. Implementing deep insert in an unmanaged RAP scenario requires meticulous planning and a clear understanding of the underlying architecture.
In this blog post, I will walk you through the process of implementing deep insert functionality in an unmanaged RAP scenario. We'll explore the data model, define the necessary CDS views, set up behavior definitions, and implement the behavior classes to handle the deep insert logic effectively. This guide aims to provide a comprehensive understanding of the steps involved, ensuring a robust and efficient implementation.
Implementation Overview
Step 1: Create Database Tables
@EndUserText.label : 'Header Order Table'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zfa_dt_order {
key order_id : char10 not null;
product : char20;
product_brand : char20;
product_quantity : char4;
}
Item Table
@EndUserText.label : 'Pricing item table'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zfa_dt_pricing {
key order_id : abap.char(10) not null;
key product_id : abap.char(5) not null;
product_price : abap.char(4);
}
Step 2: Define Interface Views
Here we have to use the key word composition and need to mention cardinality
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Interface view for header'
@Metadata.ignorePropagatedAnnotations: true
define root view entity zfa_i_Order_header as select from zfa_dt_order
composition[1..1] of zfa_i_price_item as _Pricing
{
key order_id as OrderId,
product as Product,
product_brand as ProductBrand,
product_quantity as ProductQuantity,
_Pricing // Make association public
}
Item interface view
In the item view we have to use association to parent keyword
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Interface view for item'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity zfa_i_price_item as select from ZFA_DT_PRICING
association to parent zfa_i_Order_header as _Order on $projection.OrderId = _Order.OrderId
{
key order_id as OrderId,
key product_id as ProductId,
product_price as ProductPrice,
_order
}
Step 3: Define Projection Views
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Order projection'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define root view entity zfa_P_Order_header
provider contract transactional_query as projection on zfa_i_Order_header
{
key OrderId,
Product,
ProductBrand,
ProductQuantity,
/* Associations */
_Pricing : redirected to composition child zfa_P_price_item
}
Item projection view
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Price projection view'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
define view entity zfa_P_price_item
as projection on zfa_i_price_item
{
key OrderId,
key ProductId,
ProductPrice,
/* Associations */
_order : redirected to parent zfa_P_Order_header
}
Step 4: Define Behavior Definitions
Header Interface view Behavior definition
unmanaged implementation in class zbp_fa_i_order_header unique;
strict ( 2 );
define behavior for zfa_i_Order_header //alias <alias_name>
late numbering
lock master
authorization master ( instance )
//etag master <field_name>
{
create;
update;
delete;
field ( readonly ) OrderId;
association _Pricing { create; }
mapping for zfa_dt_order
{
OrderId = order_id ;
Product = product;
ProductBrand = product_brand;
ProductQuantity = product_quantity;
}
}
define behavior for zfa_i_price_item //alias <alias_name>
late numbering
lock dependent by _order
authorization dependent by _order
//etag master <field_name>
{
create;
update;
delete;
field ( readonly ) OrderId, ProductId;
association _order;
mapping for zfa_dt_pricing{
OrderId = order_id;
ProductId = product_id;
ProductPrice = product_price;
}
}
Header Projection view Behavior Definition
projection;
strict ( 2 );
define behavior for zfa_P_Order_header //alias <alias_name>
{
use create;
use update;
use delete;
use association _Pricing { create; }
}
define behavior for zfa_P_price_item //alias <alias_name>
{
use update;
use delete;
use association _order;
}
Step 5: Implement Behavior Handler Classes
CLASS lhc_zfa_i_Order_header DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR zfa_i_Order_header RESULT result.
METHODS create FOR MODIFY
IMPORTING entities FOR CREATE zfa_i_Order_header.
METHODS update FOR MODIFY
IMPORTING entities FOR UPDATE zfa_i_Order_header.
METHODS delete FOR MODIFY
IMPORTING keys FOR DELETE zfa_i_Order_header.
METHODS read FOR READ
IMPORTING keys FOR READ zfa_i_Order_header RESULT result.
METHODS lock FOR LOCK
IMPORTING keys FOR LOCK zfa_i_Order_header.
METHODS rba_Pricing FOR READ
IMPORTING keys_rba FOR READ zfa_i_Order_header\_Pricing FULL result_requested RESULT result LINK association_links.
METHODS cba_Pricing FOR MODIFY
IMPORTING entities_cba FOR CREATE zfa_i_Order_header\_Pricing.
ENDCLASS.
CLASS lhc_zfa_i_Order_header IMPLEMENTATION.
METHOD get_instance_authorizations.
ENDMETHOD.
METHOD create.
data(lo_order) = zfa_um_helper=>get_instance( ).
lo_order->create_order(
EXPORTING
entities = entities
CHANGING
mapped = mapped
failed = failed
reported = reported
).
ENDMETHOD.
METHOD update.
ENDMETHOD.
METHOD delete.
ENDMETHOD.
METHOD read.
ENDMETHOD.
METHOD lock.
ENDMETHOD.
METHOD rba_Pricing.
ENDMETHOD.
METHOD cba_Pricing.
data(lo_pricing) = zfa_um_helper=>get_instance_price( ).
lo_pricing->create_price(
EXPORTING
entities = entities_cba
CHANGING
mapped = mapped
failed = failed
reported = reported
).
ENDMETHOD.
ENDCLASS.
CLASS lhc_zfa_i_price_item DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS update FOR MODIFY
IMPORTING entities FOR UPDATE zfa_i_price_item.
METHODS delete FOR MODIFY
IMPORTING keys FOR DELETE zfa_i_price_item.
METHODS read FOR READ
IMPORTING keys FOR READ zfa_i_price_item RESULT result.
METHODS rba_Order FOR READ
IMPORTING keys_rba FOR READ zfa_i_price_item\_Order FULL result_requested RESULT result LINK association_links.
METHODS create FOR MODIFY
IMPORTING entities FOR CREATE zfa_i_price_item.
ENDCLASS.
CLASS lhc_zfa_i_price_item IMPLEMENTATION.
METHOD update.
ENDMETHOD.
METHOD delete.
ENDMETHOD.
METHOD read.
ENDMETHOD.
METHOD rba_Order.
ENDMETHOD.
METHOD create.
ENDMETHOD.
ENDCLASS.
CLASS lsc_ZFA_I_ORDER_HEADER DEFINITION INHERITING FROM cl_abap_behavior_saver.
PROTECTED SECTION.
METHODS finalize REDEFINITION.
METHODS check_before_save REDEFINITION.
METHODS adjust_numbers REDEFINITION.
METHODS save REDEFINITION.
METHODS cleanup REDEFINITION.
METHODS cleanup_finalize REDEFINITION.
ENDCLASS.
CLASS lsc_ZFA_I_ORDER_HEADER IMPLEMENTATION.
METHOD finalize.
ENDMETHOD.
METHOD check_before_save.
ENDMETHOD.
METHOD adjust_numbers.
data(lo_order) = zfa_um_helper=>get_instance( ).
lo_order->adjust_number(
CHANGING
mapped = mapped
reported = reported
).
ENDMETHOD.
METHOD save.
data(lo_save_order) = zfa_um_helper=>get_instance( ).
lo_save_order->save_order(
CHANGING
reported = reported
).
select max( order_id ) from zfa_dt_order into table (lt_oid).
if lt_oid is not INITIAL.
data(lo_save_price) = zfa_um_helper=>get_instance_price( ).
lo_save_price->save_price(
CHANGING
reported = reported
).
endif.
ENDMETHOD.
METHOD cleanup.
ENDMETHOD.
METHOD cleanup_finalize.
ENDMETHOD.
ENDCLASS.
Step 6: Implement Helper Class for Deep Insert
This is my helper class , Here we can see our actual logic
CLASS zfa_um_helper DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
TYPES : tt_order_create TYPE TABLE FOR CREATE zfa_i_Order_header,
tt_order_cba TYPE TABLE FOR CREATE zfa_i_Order_header\_Pricing,
tt_order_delete TYPE TABLE FOR DELETE zfa_i_Order_header,
tt_order_update TYPE TABLE FOR UPDATE zfa_i_order_header,
tt_reported_early TYPE RESPONSE FOR REPORTED EARLY zfa_i_Order_header,
tt_reported_late TYPE RESPONSE FOR REPORTED LATE zfa_i_Order_header,
tt_mapped_early TYPE RESPONSE FOR MAPPED EARLY zfa_i_Order_header,
tt_mapped_late TYPE RESPONSE FOR MAPPED LATE zfa_i_Order_header,
tt_price_create TYPE TABLE FOR CREATE zfa_i_price_item,
tt_price_delete TYPE TABLE FOR DELETE zfa_i_price_item,
tt_price_update TYPE TABLE FOR UPDATE zfa_i_price_item,
tt_failed_early TYPE RESPONSE FOR FAILED EARLY zfa_i_Order_header.
CLASS-METHODS get_instance RETURNING VALUE(ret_order) TYPE REF TO zfa_um_helper.
CLASS-METHODS get_instance_price RETURNING VALUE(ret_price) TYPE REF TO zfa_um_helper.
METHODS : create_order
IMPORTING entities TYPE tt_order_create
CHANGING
mapped TYPE tt_mapped_early
failed TYPE tt_failed_early
reported TYPE tt_reported_early,
create_price
IMPORTING entities TYPE tt_order_cba
CHANGING
mapped TYPE tt_mapped_early
failed TYPE tt_failed_early
reported TYPE tt_reported_early,
adjust_number
changing mapped type tt_mapped_late
reported type tt_reported_late,
save_order
CHANGING reported TYPE tt_reported_late,
save_price
CHANGING reported TYPE tt_reported_late.
PROTECTED SECTION.
PRIVATE SECTION.
CLASS-DATA : go_order TYPE REF TO zfa_um_helper,
go_price TYPE REF TO zfa_um_helper,
gt_order_create TYPE TABLE OF zfa_dt_order,
gt_price_create TYPE TABLE OF zfa_dt_pricing,
gt_price_create1 TYPE TABLE OF zfa_i_price_item ,
gt_order_delete TYPE RANGE OF zfa_i_Order_header-OrderId,
gs_order TYPE zfa_dt_order.
ENDCLASS.
CLASS zfa_um_helper IMPLEMENTATION.
METHOD get_instance.
ret_order = go_order = COND #( WHEN go_order IS BOUND THEN go_order
ELSE NEW #( ) ).
ENDMETHOD.
METHOD get_instance_price.
ret_price = go_price = COND #( WHEN go_price IS BOUND THEN go_price
ELSE NEW #( ) ).
ENDMETHOD.
METHOD create_order.
gt_order_create = CORRESPONDING #( entities MAPPING FROM ENTITY ).
mapped-zfa_i_order_header = CORRESPONDING #( entities ).
ENDMETHOD.
METHOD create_price.
gt_price_create1 = CORRESPONDING #( entities[ 1 ]-%target ).
gt_price_create = CORRESPONDING #( gt_price_create1 MAPPING order_id = OrderId product_id = ProductId product_price = ProductPrice ).
mapped-zfa_i_price_item = CORRESPONDING #( entities ).
ENDMETHOD.
METHOD save_order.
IF gt_order_create IS NOT INITIAL.
INSERT zfa_dt_order FROM TABLE _order_create.
ENDIF.
ENDMETHOD.
METHOD save_price.
IF gt_price_create IS NOT INITIAL.
INSERT zfa_dt_pricing FROM TABLE _price_create.
ENDIF.
ENDMETHOD.
METHOD adjust_number.
data : lv_num TYPE char10,
lv_num2 TYPE char5.
if gt_order_create is NOT INITIAL.
SELECT max( order_id ) from zfa_dt_order into (lv_oid).
lv_num = COND #( when sy-subrc = 4 then 0000000001
ELSE lv_oid + 1 ).
gt_order_create[ 1 ]-order_id = lv_num .
ENDIF.
if gt_price_create is NOT INITIAL.
SELECT MAX( product_id ) from zfa_dt_pricing into (lv_pid).
lv_num2 = COND #( when sy-subrc = 4 then 00001
else lv_pid + 1 ).
gt_price_create[ 1 ]-product_id = lv_num2.
endif.
ENDMETHOD.
ENDCLASS.
Step 7: Define Metadata Annotations
After All the implementation is done we have to create the meta data files for both the projection view
Metadata for Header Projection view
@Metadata.layer:#CORE
annotate entity zfa_P_Order_header
with
{
@UI.facet: [{
purpose: #STANDARD,
position: 10,
label: 'Create Order',
type: #IDENTIFICATION_REFERENCE
},
{
purpose: #STANDARD,
position: 20,
label: 'Create payment',
type: #LINEITEM_REFERENCE,
targetElement: '_Pricing'
}]
@UI.identification: [{ position: 10 , label: 'Order ID' }]
@UI.lineItem: [{ position: 10, label: 'Order ID' }]
OrderId;
@UI.identification: [{ position: 20 , label: 'Product' }]
@UI.lineItem: [{ position: 20, label: 'Product' }]
Product;
@UI.identification: [{ position: 30 , label: 'Product Brand' }]
@UI.lineItem: [{ position: 30, label: 'Product Brand' }]
ProductBrand;
@UI.identification: [{ position: 40 , label: 'Product Quantity' }]
@UI.lineItem: [{ position: 40, label: 'Product Quality' }]
ProductQuantity;
}
Metadata for item Projection view
@Metadata.layer: #CORE
annotate entity zfa_P_price_item
with
{
@UI.facet: [{
purpose: #STANDARD,
position: 10,
label: 'Price creation',
type: #IDENTIFICATION_REFERENCE
}
]
@UI.identification: [{ position: 10 , label: 'Order ID' }]
@UI.lineItem: [{ position: 10}]
OrderId;
@UI.identification: [{ position: 20 , label: 'Product ID' }]
@UI.lineItem: [{ position: 20}]
ProductId;
@UI.identification: [{ position: 30 , label: 'Product Price' }]
@UI.lineItem: [{ position: 30 }]
ProductPrice;
}
Step 8: Create Service Definition and Binding
@EndUserText.label: 'Service def For header'
define service Zfa_def_order {
expose zfa_P_Order_header;
expose zfa_P_price_item;
}
Click on Create for creating the Header data
Here You can see header data has been created
Now click on create to create the item data for the particular header data
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
10 | |
8 | |
7 | |
6 | |
6 | |
4 | |
4 | |
4 | |
4 | |
4 |