在这篇博客中,让我们看看在BTP平台上的ABAP的管理场景(行为实现)。
正如标题中所提到的,管理场景与无/低代码概念携手并进。 由于管理方式承担了CRUD操作的所有责任,我们只需要考虑到某些授权(如果我们要实现授权对象和相关对象)。
让我们考虑以下情况。
我们有一个学生门户应用程序,有一个头表和另外两个子表。
学生头表。
@EndUserText.label : 'Header for Student'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table ystudent_hdr {
key client : abap.clnt not null;
key sid : sysuuid_x16 not null;
admission_no : abap.char(10);
fname : abap.char(25);
mname : abap.char(25);
lname : abap.char(25);
contact_no1 : abap.char(10);
contact_no2 : abap.char(10);
dob : abap.dats;
gender : ydgender;
semail : abap.char(50);
pemail : abap.char(50);
paddress : abap.string(256);
caddress : abap.string(256);
blood_group : ydbg;
height : abap.dec(4,4);
weight : abap.dec(4,4);
}
学生学业详情。
@EndUserText.label : 'Current Academic Details'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table yst_cracademic {
key client : abap.clnt not null;
@AbapCatalog.foreignKey.screenCheck : false
key sid : sysuuid_x16 not null
with foreign key ystudent_hdr
where client = yst_cracademic.client
and sid = yst_cracademic.sid;
key course : ydcourse not null;
key semester : ydsemester not null;
status : ydsemstat;
percentage : abap.dec(3,2);
feedback : abap.string(256);
}
学生出勤情况。
@EndUserText.label : 'Current Academic Details'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table yst_cracademic {
key client : abap.clnt not null;
@AbapCatalog.foreignKey.screenCheck : false
key sid : sysuuid_x16 not null
with foreign key ystudent_hdr
where client = yst_cracademic.client
and sid = yst_cracademic.sid;
key course : ydcourse not null;
key semester : ydsemester not null;
status : ydsemstat;
percentage : abap.dec(3,2);
feedback : abap.string(256);
}
以下CDS实体代表父子层次结构。
头表。
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Student Header'
@Metadata.allowExtensions: true
define root view entity YSTUDENT_HDR_I
as select from ystudent_hdr
composition [0..*] of YST_CRACADEMIC_I as _academic
composition [0..*] of YST_ATTENDANCE_I as _attendance
association to YI_GENDER as _gender on $projection.gender = _gender.Value
association to YI_BG as _bg on $projection.blood_group = _bg.Value
{
key sid,
admission_no,
fname,
mname,
lname,
contact_no1,
contact_no2,
dob,
gender,
_gender.Description as genderdesc,
semail,
pemail,
paddress,
caddress,
blood_group,
_bg.Description as bg_desc,
height,
weight,
_academic,
_attendance,
_gender,
_bg
// _association_name // Make association public
}
元数据扩展。
@Metadata.layer: #CORE
@UI: {
headerInfo: { typeName: 'Student Detail',
typeNamePlural: 'Student Details',
title: { type: #STANDARD, label: 'Student Details', value: 'fname' },
description:{ type: #STANDARD , label: 'Student Details' , value : 'lname'}
},
presentationVariant: [{ sortOrder: [{by: 'fname', direction: #ASC}] }]
}
@Search.searchable: true
annotate view YSTUDENT_HDR_I with
{
@UI.facet: [{ id: 'Student',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Student Details',
position: 10
},
{ id: 'Academic',
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Academic Details',
position: 20,
targetElement: '_academic'},
{ id: 'Attendance',
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Attendance Details',
position: 20,
targetElement: '_attendance'}]
@UI: { identification: [{ position: 10, label: 'Student ID' }] }
@UI.hidden: true
sid;
@UI: { lineItem: [{ position: 20, importance: #HIGH, label: 'Admission Number' }],
identification: [{ position: 20, label: 'Admission Number' }] }
@Search.defaultSearchElement: true
admission_no;
@UI: { lineItem: [{ position: 30, importance: #HIGH, label: 'First Name' }],
identification: [{ position: 30, label: 'First Name' }] }
@Search.defaultSearchElement: true
fname;
@UI: { lineItem: [{ position: 40, importance: #HIGH, label: 'Middle Name' }],
identification: [{ position: 40, label: 'Middle Name' }] }
mname;
@UI: { lineItem: [{ position: 50, importance: #HIGH, label: 'Last Name' }],
identification: [{ position: 50, label: 'Last Name' }] }
@Search.defaultSearchElement: true
lname;
@UI: { lineItem: [{ position: 60, importance: #HIGH, label: 'Contact Number 1' }],
identification: [{ position: 60, label: 'Contact Number 1' }] }
contact_no1;
@UI: { lineItem: [{ position: 70, importance: #HIGH, label: 'Contact Number 2' }],
identification: [{ position: 70, label: 'Contact Number 2' }] }
contact_no2;
@UI: { lineItem: [{ position: 80, importance: #HIGH, label: 'Date of Birth' }],
identification: [{ position: 80, label: 'Date of Birth' }] }
dob;
@UI: { lineItem: [{ position: 90, importance: #HIGH, label: 'Gender' }],
identification: [{ position: 90, label: 'Gender' }] }
@Consumption.valueHelpDefinition: [{ entity:
{name: 'YI_GENDER' , element: 'Value' },
additionalBinding: [{ localElement: 'genderdesc', element: 'Description' }]
}]
gender;
@UI: { lineItem: [{ position: 100, importance: #HIGH, label: '' }],
identification: [{ position: 100, label: '' }] }
genderdesc;
@UI: { lineItem: [{ position: 110, importance: #HIGH, label: 'Student Email' }],
identification: [{ position: 110, label: 'Student Email' }] }
semail;
@UI: { lineItem: [{ position: 120, importance: #HIGH, label: 'Parent Email' }],
identification: [{ position: 120, label: 'Parent Email' }] }
pemail;
@UI: { lineItem: [{ position: 130, importance: #HIGH, label: 'Parent Address' }],
identification: [{ position: 130, label: 'Parent Address' }] }
paddress;
@UI: { lineItem: [{ position: 140, importance: #HIGH, label: 'Student Address ' }],
identification: [{ position: 140, label: 'Student Address ' }] }
caddress;
@UI: { lineItem: [{ position: 150, importance: #HIGH, label: 'Blood Group' }],
identification: [{ position: 150, label: 'Blood Group' }] }
@Consumption.valueHelpDefinition: [{ entity:
{name: 'YI_BG' , element: 'Value' },
additionalBinding: [{ localElement: 'bg_desc', element: 'Description' }]
}]
blood_group;
@UI: { lineItem: [{ position: 160, importance: #HIGH, label: '' }],
identification: [{ position: 160, label: '' }] }
bg_desc;
@UI: { lineItem: [{ position: 170, importance: #HIGH, label: 'Height in CM' }],
identification: [{ position: 170, label: 'Height in CM' }] }
height;
@UI: { lineItem: [{ position: 180, importance: #HIGH, label: 'Weight in KG' }],
identification: [{ position: 180, label: 'Weight in KG' }] }
weight;
}
学业细节。
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Academic Details'
@Metadata.allowExtensions: true
define view entity YST_CRACADEMIC_I
as select from yst_cracademic
association to parent YSTUDENT_HDR_I as _HDR on $projection.sid = _HDR.sid
association to YI_COURSE as _course on $projection.course = _course.Value
association to YI_SEMESTER as _semester on $projection.semester = _semester.Value
association to YI_SEMSTAT as _stat on $projection.status = _stat.Value
{
key sid,
key course,
key semester,
_course.Description as course_desc,
_semester.Description as semester_desc,
status,
_stat.Description as semester_status,
percentage,
feedback,
_HDR // Make association public
}
元数据扩展。
@Metadata.layer: #CORE
@UI: {
headerInfo: { typeName: 'Academic Detail',
typeNamePlural: 'Academic Details',
title: { type: #STANDARD, label: 'Academic Details', value: 'course' },
description:{ type: #STANDARD , label: 'Academic Details' , value : 'course_desc'}
},
presentationVariant: [{ sortOrder: [{by: 'semester', direction: #ASC}] }]
}
@Search.searchable: true
annotate view YST_CRACADEMIC_I with
{
@UI.facet: [{ id: 'Acadmic',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Academic Details',
position: 10
}]
@UI: { identification: [{ position: 10, label: 'Student ID' }] }
@UI.hidden: true
sid;
@UI: { lineItem: [{ position: 20, importance: #HIGH, label: 'Course' }],
identification: [{ position: 20, label: 'Course' }] }
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity:
{name: 'YI_COURSE' , element: 'Value' },
additionalBinding: [{ localElement: 'course_desc', element: 'Description' }]
}]
course;
@UI: { lineItem: [{ position: 30, importance: #HIGH, label: '' }],
identification: [{ position: 30, label: '' }] }
@Search.defaultSearchElement: true
course_desc;
@UI: { lineItem: [{ position: 40, importance: #HIGH, label: 'Semester' }],
identification: [{ position: 40, label: 'Semester' }] }
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity:
{name: 'YI_SEMESTER' , element: 'Value' },
additionalBinding: [{ localElement: 'semester_desc', element: 'Description' }]
}]
semester;
@UI: { lineItem: [{ position: 50, importance: #HIGH, label: '' }],
identification: [{ position: 50, label: '' }] }
@Search.defaultSearchElement: true
semester_desc;
@UI: { lineItem: [{ position: 60, importance: #HIGH, label: 'Status' }],
identification: [{ position: 60, label: 'Status' }] }
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity:
{name: 'YI_SEMSTAT' , element: 'Value' },
additionalBinding: [{ localElement: 'semester_status', element: 'Description' }]
}]
status;
@UI: { lineItem: [{ position: 70, importance: #HIGH, label: '' }],
identification: [{ position: 70, label: '' }] }
@Search.defaultSearchElement: true
semester_status;
@UI: { lineItem: [{ position: 80, importance: #HIGH, label: 'Percentage Scored' }],
identification: [{ position: 80, label: 'Percentage Scored' }] }
@Search.defaultSearchElement: true
percentage;
@UI: { lineItem: [{ position: 90, importance: #HIGH, label: 'Student Feed Back' }],
identification: [{ position: 90, label: 'Student Feed Back' }] }
@Search.defaultSearchElement: true
feedback;
}
出勤情况 : Metadata Extension:
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Attendance'
@Metadata.allowExtensions: true
define view entity YST_ATTENDANCE_I
as select from yst_attendance
association to parent YSTUDENT_HDR_I as _hdr on $projection.sid = _hdr.sid
association to YI_COURSE as _course on $projection.course = _course.Value
association to YI_SEMESTER as _semester on $projection.semester = _semester.Value
association to YI_SEMSTAT as _stat on $projection.status = _stat.Value
{
key sid,
key course,
key semester,
_course.Description as course_desc,
_semester.Description as semester_desc,
status,
_stat.Description as semester_status,
percentage,
feedback,
_hdr // Make association public
}
元数据扩展 :学籍资料 : 学籍资料
@Metadata.layer: #CORE
@UI: {
headerInfo: { typeName: 'Attendance Detail',
typeNamePlural: 'Attendance Details',
title: { type: #STANDARD, label: 'Attendance Detail', value: 'course' },
description:{ type: #STANDARD , label: 'Attendance Details' , value : 'course_desc'}
},
presentationVariant: [{ sortOrder: [{by: 'semester', direction: #ASC}] }]
}
@Search.searchable: true
annotate view YST_ATTENDANCE_I
with
{
@UI.facet: [{ id: 'Attendance',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Attendance Details',
position: 10
}]
@UI: { identification: [{ position: 10, label: 'Student ID' }] }
@UI.hidden: true
sid;
@UI: { lineItem: [{ position: 20, importance: #HIGH, label: 'Course' }],
identification: [{ position: 20, label: 'Course' }] }
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity:
{name: 'YI_COURSE' , element: 'Value' },
additionalBinding: [{ localElement: 'course_desc', element: 'Description' }]
}]
course;
@UI: { lineItem: [{ position: 30, importance: #HIGH, label: '' }],
identification: [{ position: 30, label: '' }] }
@Search.defaultSearchElement: true
course_desc;
@UI: { lineItem: [{ position: 40, importance: #HIGH, label: 'Semester' }],
identification: [{ position: 40, label: 'Semester' }] }
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity:
{name: 'YI_SEMESTER' , element: 'Value' },
additionalBinding: [{ localElement: 'semester_desc', element: 'Description' }]
}]
semester;
@UI: { lineItem: [{ position: 50, importance: #HIGH, label: '' }],
identification: [{ position: 50, label: '' }] }
@Search.defaultSearchElement: true
semester_desc;
@UI: { lineItem: [{ position: 60, importance: #HIGH, label: 'Status' }],
identification: [{ position: 60, label: 'Status' }] }
@Search.defaultSearchElement: true
@Consumption.valueHelpDefinition: [{ entity:
{name: 'YI_SEMSTAT' , element: 'Value' },
additionalBinding: [{ localElement: 'semester_status', element: 'Description' }]
}]
status;
@UI: { lineItem: [{ position: 70, importance: #HIGH, label: '' }],
identification: [{ position: 70, label: '' }] }
@Search.defaultSearchElement: true
semester_status;
@UI: { lineItem: [{ position: 80, importance: #HIGH, label: 'Percentage Scored' }],
identification: [{ position: 80, label: 'Percentage Scored' }] }
@Search.defaultSearchElement: true
percentage;
@UI: { lineItem: [{ position: 90, importance: #HIGH, label: 'Student Feed Back' }],
identification: [{ position: 90, label: 'Student Feed Back' }] }
@Search.defaultSearchElement: true
feedback;
}
少数其他用于搜索帮助的CDS。
血型。
@AbapCatalog.sqlViewName: 'YIBG'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Gender'
define view YI_BG as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T( p_domain_name: 'YBG' )
{
key domain_name,
key value_position,
@Semantics.language: true
key language,
value_low as Value,
@Semantics.text: true
text as Description
}
领域 :

课程。
@AbapCatalog.sqlViewName: 'YICOURSE'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Course'
define view YI_COURSE
as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T( p_domain_name: 'YCOURSE' )
{
key domain_name,
key value_position,
@Semantics.language: true
key language,
value_low as Value,
@Semantics.text: true
text as Description
}
域:域名。

性别。
@AbapCatalog.sqlViewName: 'YGEN'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Gender'
define view YI_GENDER as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T( p_domain_name: 'YGENDER' )
{
key domain_name,
key value_position,
@Semantics.language: true
key language,
value_low as Value,
@Semantics.text: true
text as Description
}
范畴 : 医学领域 :

学期。
@AbapCatalog.sqlViewName: 'YISEM'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Semester'
define view YI_SEMESTER
as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T( p_domain_name: 'YSEMESTER' )
{
key domain_name,
key value_position,
@Semantics.language: true
key language,
value_low as Value,
@Semantics.text: true
text as Description
}
范畴 : 硕士

状况 :
@AbapCatalog.sqlViewName: 'YISTAT'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Status'
define view YI_SEMSTAT
as select from DDCDS_CUSTOMER_DOMAIN_VALUE_T( p_domain_name: 'YSEMSTAT' )
{
key domain_name,
key value_position,
@Semantics.language: true
key language,
value_low as Value,
@Semantics.text: true
text as Description
}
领域 : 状况 :

行为定义
managed implementation in class YST_BEHAVIOUR unique;
strict;
define behavior for YSTUDENT_HDR_I
persistent table ystudent_hdr
lock master
authorization master ( instance )
{
field ( numbering : managed, readonly ) sid;
create;
update;
delete;
field ( readonly ) genderdesc;
field ( readonly ) bg_desc;
association _attendance { create; }
association _academic { create; }
}
define behavior for YST_ATTENDANCE_I
persistent table yst_attendance
lock dependent by _hdr
authorization dependent by _hdr
{
update;
delete;
field ( readonly ) sid;
field ( readonly ) course_desc;
field ( readonly ) semester_desc;
field ( readonly ) semester_status;
association _hdr;
}
define behavior for YST_CRACADEMIC_I
persistent table yst_cracademic
lock dependent by _HDR
authorization dependent by _HDR
{
update;
delete;
field ( readonly ) sid;
field ( readonly ) course_desc;
field ( readonly ) semester_desc;
field ( readonly ) semester_status;
association _HDR;
}
行为的实施。
****Global Class ************
CLASS yst_behaviour DEFINITION
PUBLIC
abstract
final
for behavior of YSTUDENT_HDR_I .
PUBLIC SECTION.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS yst_behaviour IMPLEMENTATION.
ENDCLASS.
****Local Types******
CLASS YST_behaviour2 DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR ystudent_hdr_i RESULT result.
ENDCLASS.
CLASS YST_behaviour2 IMPLEMENTATION.
METHOD get_instance_authorizations.
********Your code goes here
ENDMETHOD.
ENDCLASS.
如果我们看一下这里的行为类,我们几乎没有写28行代码,如果我们在一个服务定义中暴露CDS并创建一个服务绑定,所有的父子的CRUD操作都可以正常工作。所有的CRUD操作代码都被系统照顾到了,我们只需付出最小的努力。
由于SAP推荐在CDS中使用视图实体,我们必须创建这个类,如果我们只使用视图,这个类是可选的。
让我们看一下屏幕。


注:该博客是基于我对BTP的研究和发展而写的,如果我提到任何遗漏的信息,请在评论中纠正我。