はじめに
この記事では、RAP(ABAP RESTful Application Programming Model)で作成したUnmanaged BOにUpdate処理を追加します。
シリーズの先頭はこちら☞ 【RAP】Unmanaged BOの実装 (1)
もともとドラフトありで作成していたのですが、Editボタンを押したときにエラーになるという問題があり、ここからはドラフトなしで進めます。ドラフトなしに変える方法は以下の通りです。
・Behavior Projectionでドラフトに関連する箇所をコメントアウト
projection;
//use draft;
define behavior for ZC_PERSON_U alias Person
{
use create;
use update;
use delete;
// use action Edit;
// use action Activate;
// use action Discard;
// use action Resume;
// use action Prepare;
// use association _FamilyMember { create; with draft; }
use association _FamilyMember { create; }
}
define behavior for ZC_FAMILYMEMBER_U alias FamilyMember
{
use update;
use delete;
// use association _Person { with draft; }
use association _Person { }
}
OData V2のService Bindingを作成
OData V4に対応したList Reportでは、ドラフトありでないと登録、編集ボタンが表示されないため、V2版を作成します。簡単にODataバージョンを切り替えられるのがRAPの良いところです。
処理概要
現在の構成は以下のようになっています。Behavior Implementationクラスから、実際の処理を行うAdapterクラスのメソッドが呼ばれます。Adapterクラスのローカルクラスではトランザクションバッファとのやり取りを行い、SAVEのタイミングでバッファから取り出したレコードをDBに格納します。
今回、UPDATEの処理を追加するとともに、SAVEの処理を変更します。
実装ステップ
- Adapterクラスの実装
- Behavior Implementationクラスの実装
1. Adapterクラスの実装
1.1. インターフェースに型を追加
更新処理で使用する型を追加します。ts_person_controlおよびts_familymember_controlは、更新された項目を識別するためのフラグを持った型です。
TYPES tt_person TYPE SORTED TABLE OF zperson_u WITH UNIQUE KEY person_id.
TYPES tt_familymember TYPE SORTED TABLE OF zfamilymember_u WITH UNIQUE KEY person_id member_id.
TYPES BEGIN OF ts_person_control.
TYPES person_id TYPE ze_person_id.
INCLUDE TYPE zsperson_x_u.
TYPES END OF ts_person_control.
TYPES BEGIN OF ts_familymember_control.
TYPES person_id TYPE ze_person_id.
TYPES member_id TYPE ze_member_id.
INCLUDE TYPE zsfamilymember_x_u.
TYPES END OF ts_familymember_control.
zsperson_x_u, zsfamilymember_x_uはRAP Generatorで自動生成された構造です。テーブルの項目名と同じ名称で、xsdbooleanの型を持つ項目を持っています。
1.2. ローカルクラスの実装
バッファ更新用のメソッドを追加します。また、save
メソッドに更新処理を追加します。
メソッド定義
CLASS lcl_person_buffer DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
" Buffer Tables
...
"更新用のバッファテーブル
data mt_update_buffer_person type zif_person_adapter=>tt_person.
data mt_update_buffer_familymember type zif_person_adapter=>tt_familymember.
METHODS buffer_person_for_update IMPORTING is_person type zperson_u.
METHODS buffer_familymember_for_update IMPORTING is_familymember type zfamilymember_u.
メソッド実装
METHOD buffer_person_for_update.
CHECK is_person is not initial.
insert is_person into table mt_update_buffer_person.
ENDMETHOD.
METHOD buffer_familymember_for_update.
CHECK is_familymember is not initial.
insert is_familymember into table mt_update_buffer_familymember.
ENDMETHOD.
METHOD save.
data(lt_perton) = get_person_create( ).
data(lt_familymember) = get_familymember_create( ).
INSERT zperson_u FROM TABLE @lt_perton.
UPDATE zperson_u FROM TABLE @mt_update_buffer_person. "追加
INSERT zfamilymember_u FROM TABLE @lt_familymember.
UPDATE zfamilymember_u FROM TABLE @mt_update_buffer_familymember. "追加
ENDMETHOD.
1.3. グローバルクラスの実装
Behavior Implementationクラスから呼び出されるメソッドを実装します。Behavior Implementationクラスのupdate
メソッドには、更新された項目しか渡されません。更新された項目にはフラグが設定されるので、フラグをもとにどの項目が更新されたか判断し、更新されていない項目はDBから取得して埋める必要があります。また、ドラフトなしにしたことに伴って更新日、更新ユーザ等もここで設定します。
メソッド定義
METHODS update_person IMPORTING is_person TYPE zperson_u
is_person_control TYPE zif_person_adapter=>ts_person_control.
METHODS update_familymember IMPORTING is_familymember TYPE zfamilymember_u
is_familymember_control TYPE zif_person_adapter=>ts_familymember_control.
メソッド実装
METHOD update_person.
"TODO: clientの扱い
"データベースから全項目を取得
SELECT SINGLE * FROM zperson_u
WHERE person_id = @is_person-person_id
INTO @DATA(ls_person).
"変更された項目で上書き
DATA index TYPE i.
index = 2. "キーを飛ばして2番目の項目から
DO.
ASSIGN COMPONENT index OF STRUCTURE is_person_control TO FIELD-SYMBOL(<is_changed>).
IF sy-subrc <> 0.
EXIT.
ENDIF.
IF <is_changed> = abap_true.
"clientつきの構造のため、indexを1つ進める
ASSIGN COMPONENT index + 1 OF STRUCTURE is_person TO FIELD-SYMBOL(<field_upd>).
ASSERT sy-subrc = 0.
ASSIGN COMPONENT index + 1 OF STRUCTURE ls_person TO FIELD-SYMBOL(<field_current>).
ASSERT sy-subrc = 0.
<field_current> = <field_upd>.
ENDIF.
index = index + 1.
ENDDO.
"タイムスタンプ等の設定
ls_person-last_changed_by = sy-uname.
GET TIME STAMP FIELD ls_person-last_changed_at.
GET TIME STAMP FIELD ls_person-local_last_changed_at.
lcl_person_buffer=>get_instance( )->buffer_person_for_update( ls_person ).
ENDMETHOD.
METHOD update_familymember.
"データベースから全項目を取得
SELECT SINGLE * FROM zfamilymember_u
WHERE person_id = @is_familymember-person_id
AND member_id = @is_familymember-member_id
INTO @DATA(ls_familymember).
DATA index TYPE i.
index = 3. "キーを飛ばして3番目の項目から
DO.
ASSIGN COMPONENT index OF STRUCTURE is_familymember_control TO FIELD-SYMBOL(<is_changed>).
IF sy-subrc <> 0.
EXIT.
ENDIF.
IF <is_changed> = abap_true.
"clientつきの構造のため、indexを1つ進める
ASSIGN COMPONENT index + 1 OF STRUCTURE is_familymember TO FIELD-SYMBOL(<field_upd>).
ASSERT sy-subrc = 0.
ASSIGN COMPONENT index + 1 OF STRUCTURE ls_familymember TO FIELD-SYMBOL(<field_current>).
ASSERT sy-subrc = 0.
<field_current> = <field_upd>.
ENDIF.
index = index + 1.
ENDDO.
"タイムスタンプの設定
GET TIME STAMP FIELD ls_familymember-last_changed_at.
lcl_person_buffer=>get_instance( )->buffer_familymember_for_update( ls_familymember ).
ENDMETHOD.
2. Behavior Implementationクラスの実装
Behavior Implentationクラスでは、受け取ったEntityおよびコントロール構造(更新された項目にフラグがセットされたもの)をAdapterクラスのメソッドに渡します。MAPPING FROM ENTITY
というステートメントは、Behavior Definitionで定義したマッピングの通りにエンティティの項目をDBの項目にマッピングしてくれます。
Behavior Definitionでのマッピングは以下のようになっています。control ZSPerson_X_u
を指定することにより、コントロール構造のフラグ(00 or 01)をbooleanに変換することができます。
mapping for ZPERSON_U control ZSPerson_X_u
{
PersonID = PERSON_ID;
FirstName = FIRST_NAME;
LastName = LAST_NAME;
Email = EMAIL;
Birthday = BIRTHDAY;
Status = STATUS;
CreatedBy = CREATED_BY;
CreatedAt = CREATED_AT;
LastChangedBy = LAST_CHANGED_BY;
LocalLastChangedAt = LOCAL_LAST_CHANGED_AT;
LastChangedAt = LAST_CHANGED_AT;
}
Person
METHOD update.
DATA person_u TYPE zperson_u.
DATA person_ux TYPE ZSPerson_X_u.
data person_control type zif_person_adapter=>ts_person_control.
LOOP AT entities ASSIGNING FIELD-SYMBOL(<person>).
person_u = CORRESPONDING #( <person> MAPPING FROM ENTITY ).
person_ux = CORRESPONDING #( <person> MAPPING FROM ENTITY ).
person_control = CORRESPONDING #( person_ux ).
person_control-person_id = <person>-PersonID.
zcl_person_adapter=>get_instance( )->update_person(
EXPORTING
is_person = person_u
is_person_control = person_control
).
ENDLOOP.
ENDMETHOD.
Family Member
METHOD update.
DATA familymember_u TYPE zfamilymember_u.
DATA familymember_ux TYPE ZSFamilyMember_X_u.
data familymember_control type zif_person_adapter=>ts_familymember_control.
LOOP AT entities ASSIGNING FIELD-SYMBOL(<member>).
familymember_u = CORRESPONDING #( <member> MAPPING FROM ENTITY ).
familymember_ux = CORRESPONDING #( <member> MAPPING FROM ENTITY ).
familymember_control = CORRESPONDING #( familymember_ux ).
familymember_control-person_id = <member>-PersonID.
familymember_control-member_id = <member>-MemberID.
zcl_person_adapter=>get_instance( )->update_familymember(
EXPORTING
is_familymember = familymember_u
is_familymember_control = familymember_control
).
ENDLOOP.
ENDMETHOD.