A better way to create the Root-Child-Grandchild hierachy in RAP

The RAP Framework supports Parent – Child – Grandchild – Great Grandchild – and so forth.

There are beautiful blog posts on how to do this:

How to design managed RAP business objects with 3 or more levels of nodes : Shows the basic idea.Create Root-Child-Grandchild-Great Grandchild relationship using RAP : Shows the actual implementation.

Both of them relies on storing a reference to the root in each and every descendant. As in: a child has a reference to the parent, and a grandchild (or great grandchild) has a reference to the root parent as well.

This is required so that in the Behavior Definition of each descendant, the association to the root can be used for `lock` and `authorization`.

But this has got me thinking: Why do we have to store the reference to the root in each descendant like this? Couldn’t we do something like:

Grandchild._Child._Parent
“or even
GreatGrandchild._Grandchild._Child._Parent

 If this worked, we would need to store the reference to the parent only in the child, therefore saving space on Grandchild and Great Grandchild.

With that said, let’s dive in and test this. Here I only build Parent, Child, and Grandchild.

Database tables

Parent:

@EndUserText.label : ‘parent’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table z_parenttest {

key client : abap.clnt not null;
key uuid : sysuuid_x16 not null;
local_created_by : abp_creation_user;
local_created_at : abp_creation_tstmpl;
local_last_changed_by : abp_locinst_lastchange_user;
local_last_changed_at : abp_locinst_lastchange_tstmpl;
last_changed_at : abp_lastchange_tstmpl;

}

 Child:

@EndUserText.label : ‘CHILD’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table z_childtest {

key client : abap.clnt not null;
key uuid : sysuuid_x16 not null;
parent_uuid : sysuuid_x16;

}

 Grandchild:

@EndUserText.label : ‘Grandchild’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table z_gchildtest {

key client : abap.clnt not null;
key uuid : sysuuid_x16 not null;
child_uuid : sysuuid_x16;

}

As you can see, the grandchild doesn’t have a field for the parent’s UUID.

Interface View

Parent

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘PARENT’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZI_PARENT
as select from z_parenttest
composition [0..*] of zi_child as _Child
{
key uuid as Uuid,
@Semantics.user.createdBy: true
local_created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
local_created_at as CreatedAt,
@Semantics.user.lastChangedBy: true
local_last_changed_by as LastChangedBy,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt,
_Child
}

 Child

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘child’
@Metadata.ignorePropagatedAnnotations: true
define view entity zi_child
as select from z_childtest
association to parent ZI_PARENT as _Parent on $projection.ParentUuid = _Parent.Uuid
composition [0..*] of ZI_GRANDCHILD as _GrandChild
{
key uuid as Uuid,
parent_uuid as ParentUuid,
_Parent,
_GrandChild
}

Everything looks normal so far. Now for what we came here for: The Grandchild!

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘GRANDE’
@Metadata.ignorePropagatedAnnotations: true
define view entity ZI_GRANDCHILD
as select from z_gchildtest
association to parent zi_child as _Child on $projection.ChildUuid = _Child.Uuid
{
key uuid as Uuid,
child_uuid as ChildUuid,
_Child,
//You will get an error saying you have to include this
//If you want to use _Child._Parent
_Child.ParentUuid as ParentUUID,
_Child._Parent as _Parent,

}

 And just like that, the association to parent is made, without having to store a reference.

From this point onward: Projection View, MDE, Behavior… Everything should be the same as in the mentioned blogs.

Thanks for reading my blog and I wish you a pleasant day.

 

​ The RAP Framework supports Parent – Child – Grandchild – Great Grandchild – and so forth.There are beautiful blog posts on how to do this:How to design managed RAP business objects with 3 or more levels of nodes : Shows the basic idea.Create Root-Child-Grandchild-Great Grandchild relationship using RAP : Shows the actual implementation.Both of them relies on storing a reference to the root in each and every descendant. As in: a child has a reference to the parent, and a grandchild (or great grandchild) has a reference to the root parent as well.This is required so that in the Behavior Definition of each descendant, the association to the root can be used for `lock` and `authorization`.But this has got me thinking: Why do we have to store the reference to the root in each descendant like this? Couldn’t we do something like:Grandchild._Child._Parent
“or even
GreatGrandchild._Grandchild._Child._Parent If this worked, we would need to store the reference to the parent only in the child, therefore saving space on Grandchild and Great Grandchild.With that said, let’s dive in and test this. Here I only build Parent, Child, and Grandchild.Database tablesParent:@EndUserText.label : ‘parent’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table z_parenttest {

key client : abap.clnt not null;
key uuid : sysuuid_x16 not null;
local_created_by : abp_creation_user;
local_created_at : abp_creation_tstmpl;
local_last_changed_by : abp_locinst_lastchange_user;
local_last_changed_at : abp_locinst_lastchange_tstmpl;
last_changed_at : abp_lastchange_tstmpl;

} Child:@EndUserText.label : ‘CHILD’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table z_childtest {

key client : abap.clnt not null;
key uuid : sysuuid_x16 not null;
parent_uuid : sysuuid_x16;

} Grandchild:@EndUserText.label : ‘Grandchild’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table z_gchildtest {

key client : abap.clnt not null;
key uuid : sysuuid_x16 not null;
child_uuid : sysuuid_x16;

}As you can see, the grandchild doesn’t have a field for the parent’s UUID.Interface ViewParent@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘PARENT’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZI_PARENT
as select from z_parenttest
composition [0..*] of zi_child as _Child
{
key uuid as Uuid,
@Semantics.user.createdBy: true
local_created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
local_created_at as CreatedAt,
@Semantics.user.lastChangedBy: true
local_last_changed_by as LastChangedBy,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt,
_Child
} Child@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘child’
@Metadata.ignorePropagatedAnnotations: true
define view entity zi_child
as select from z_childtest
association to parent ZI_PARENT as _Parent on $projection.ParentUuid = _Parent.Uuid
composition [0..*] of ZI_GRANDCHILD as _GrandChild
{
key uuid as Uuid,
parent_uuid as ParentUuid,
_Parent,
_GrandChild
}Everything looks normal so far. Now for what we came here for: The Grandchild!@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘GRANDE’
@Metadata.ignorePropagatedAnnotations: true
define view entity ZI_GRANDCHILD
as select from z_gchildtest
association to parent zi_child as _Child on $projection.ChildUuid = _Child.Uuid
{
key uuid as Uuid,
child_uuid as ChildUuid,
_Child,
//You will get an error saying you have to include this
//If you want to use _Child._Parent
_Child.ParentUuid as ParentUUID,
_Child._Parent as _Parent,

} And just like that, the association to parent is made, without having to store a reference.From this point onward: Projection View, MDE, Behavior… Everything should be the same as in the mentioned blogs.Thanks for reading my blog and I wish you a pleasant day.   Read More Technology Blog Posts by Members articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author