Das Singleton Pattern in RAP ermöglicht Business Objects, die genau eine Instanz haben. Dies ist ideal für anwendungsweite Einstellungen, Konfigurationen oder globale Parameter, die zentral verwaltet werden sollen.
Grundkonzept
Ein Singleton im RAP-Kontext ist ein Business Object, das:
- Genau eine Instanz haben darf
- Nicht gelöscht werden kann (die Instanz existiert immer)
- Keine neuen Instanzen erstellen lässt
- Nur Update-Operationen erlaubt
| Normales BO | Singleton BO |
|---|---|
| Beliebig viele Instanzen | Genau eine Instanz |
| CREATE, UPDATE, DELETE | Nur UPDATE |
| Eigener Schlüssel | Fester/virtueller Schlüssel |
| Liste von Objekten | Ein globales Objekt |
Anwendungsfälle
Das Singleton Pattern eignet sich für:
- Systemeinstellungen: Globale Konfigurationsparameter
- Anwendungskonfiguration: Feature Flags, Schwellwerte
- Mandanteneinstellungen: Firmenspezifische Defaults
- Zähler und Statistiken: Anwendungsweite Metriken
- Letzter Zustand: “Current” Objekte (aktueller Abrechnungszeitraum)
Implementierung
1. Datenbanktabelle
Die Tabelle hat typischerweise einen festen Schlüsselwert:
@EndUserText.label : 'Application Settings'@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE@AbapCatalog.tableCategory : #TRANSPARENT@AbapCatalog.deliveryClass : #Cdefine table zappsettings { key client : abap.clnt not null; key singleton_key : abap.char(1) not null; " Immer 'X' max_items : abap.int4; default_currency : abap.cuky; enable_feature_a : abap_boolean; enable_feature_b : abap_boolean; last_changed_at : timestampl; last_changed_by : abap.uname;}2. CDS View für Singleton
Der entscheidende Unterschied ist die Annotation @ObjectModel.singleton.element:
@AccessControl.authorizationCheck: #NOT_REQUIRED@EndUserText.label: 'Application Settings'define root view entity ZI_AppSettings as select from zappsettings{ key singleton_key as SingletonKey,
max_items as MaxItems, default_currency as DefaultCurrency, enable_feature_a as EnableFeatureA, enable_feature_b as EnableFeatureB,
@Semantics.systemDateTime.lastChangedAt: true last_changed_at as LastChangedAt,
@Semantics.user.lastChangedBy: true last_changed_by as LastChangedBy}3. Behavior Definition mit Singleton
Die Behavior Definition deklariert das Singleton mit dem Keyword singleton:
managed implementation in class zbp_i_appsettings unique;strict ( 2 );
define behavior for ZI_AppSettings alias Settingspersistent table zappsettingslock masterauthorization master ( instance ){ // Singleton-Deklaration static singleton;
// Nur Update erlaubt - kein Create/Delete update;
// Feldmapping field ( readonly ) SingletonKey; field ( readonly ) LastChangedAt, LastChangedBy;
// Automatische Feldbefüllung mapping for zappsettings corresponding { SingletonKey = singleton_key; MaxItems = max_items; DefaultCurrency = default_currency; EnableFeatureA = enable_feature_a; EnableFeatureB = enable_feature_b; LastChangedAt = last_changed_at; LastChangedBy = last_changed_by; }}4. Singleton automatisch initialisieren
Da ein Singleton immer existieren muss, wird es beim ersten Zugriff automatisch erstellt. Dies geschieht über eine read Erweiterung:
managed implementation in class zbp_i_appsettings unique;strict ( 2 );
define behavior for ZI_AppSettings alias Settingspersistent table zappsettingslock masterauthorization master ( instance ){ static singleton;
update;
// Erlaubt die automatische Initialisierung internal create;
field ( readonly ) SingletonKey;}5. Behavior Implementation
Die Implementation stellt sicher, dass das Singleton existiert:
CLASS zbp_i_appsettings DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zi_appsettings.ENDCLASS.
CLASS zbp_i_appsettings IMPLEMENTATION.ENDCLASS.
CLASS lhc_settings DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR Settings RESULT result.
METHODS read FOR READ IMPORTING keys FOR READ Settings RESULT result.ENDCLASS.
CLASS lhc_settings IMPLEMENTATION.
METHOD get_instance_authorizations. " Alle dürfen lesen und ändern (hier Berechtigungsprüfung einbauen) LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>). APPEND VALUE #( %tky = <key>-%tky %update = if_abap_behv=>auth-allowed ) TO result. ENDLOOP. ENDMETHOD.
METHOD read. " Prüfen ob Singleton existiert SELECT SINGLE * FROM zappsettings WHERE singleton_key = 'X' INTO @DATA(ls_settings).
IF sy-subrc <> 0. " Singleton existiert nicht - initial anlegen INSERT zappsettings FROM @( VALUE #( singleton_key = 'X' max_items = 100 default_currency = 'EUR' ) ).
" Erneut lesen SELECT SINGLE * FROM zappsettings WHERE singleton_key = 'X' INTO @ls_settings. ENDIF.
" Ergebnis zurückgeben result = VALUE #( ( SingletonKey = ls_settings-singleton_key MaxItems = ls_settings-max_items DefaultCurrency = ls_settings-default_currency EnableFeatureA = ls_settings-enable_feature_a EnableFeatureB = ls_settings-enable_feature_b LastChangedAt = ls_settings-last_changed_at LastChangedBy = ls_settings-last_changed_by ) ). ENDMETHOD.
ENDCLASS.Singleton mit Draft
Für komplexere Einstellungen kann Draft Handling aktiviert werden:
managed implementation in class zbp_i_appsettings unique;strict ( 2 );with draft;
define behavior for ZI_AppSettings alias Settingspersistent table zappsettingsdraft table zappsettings_dlock masterauthorization master ( instance ){ static singleton;
update; internal create;
draft action Edit; draft action Activate optimized; draft action Discard; draft action Resume; draft determine action Prepare;
field ( readonly ) SingletonKey;}Die Draft-Tabelle:
@EndUserText.label : 'Draft: Application Settings'@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE@AbapCatalog.tableCategory : #TRANSPARENTdefine table zappsettings_d { key client : abap.clnt not null; key singleton_key : abap.char(1) not null; max_items : abap.int4; default_currency : abap.cuky; enable_feature_a : abap_boolean; enable_feature_b : abap_boolean; // Draft Admin Felder "%admin" : include sych_bdl_draft_admin_inc;}Projection View
Die Projection für die Fiori App:
@AccessControl.authorizationCheck: #NOT_REQUIRED@EndUserText.label: 'App Settings'@Metadata.allowExtensions: truedefine root view entity ZC_AppSettings provider contract transactional_query as projection on ZI_AppSettings{ key SingletonKey,
MaxItems, DefaultCurrency, EnableFeatureA, EnableFeatureB,
LastChangedAt, LastChangedBy}Projection Behavior
projection;strict ( 2 );use draft;
define behavior for ZC_AppSettings alias Settings{ use update;
use action Edit; use action Activate; use action Discard; use action Resume; use action Prepare;}UI-Annotation für Object Page
Ein Singleton wird typischerweise als Object Page ohne vorherige Liste angezeigt:
@Metadata.layer: #CUSTOMERannotate entity ZC_AppSettings with{ @UI.facet: [ { id: 'GeneralSettings', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, label: 'Allgemeine Einstellungen', position: 10, targetQualifier: 'General' }, { id: 'Features', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, label: 'Feature Flags', position: 20, targetQualifier: 'Features' } ]
@UI.fieldGroup: [{ qualifier: 'General', position: 10 }] MaxItems;
@UI.fieldGroup: [{ qualifier: 'General', position: 20 }] DefaultCurrency;
@UI.fieldGroup: [{ qualifier: 'Features', position: 10 }] EnableFeatureA;
@UI.fieldGroup: [{ qualifier: 'Features', position: 20 }] EnableFeatureB;}Singleton vom Code konsumieren
EML Zugriff
" Singleton lesenREAD ENTITIES OF zi_appsettings ENTITY Settings ALL FIELDS WITH VALUE #( ( SingletonKey = 'X' ) ) RESULT DATA(lt_settings).
DATA(ls_settings) = lt_settings[ 1 ].
" Singleton ändernMODIFY ENTITIES OF zi_appsettings ENTITY Settings UPDATE FIELDS ( MaxItems EnableFeatureA ) WITH VALUE #( ( SingletonKey = 'X' MaxItems = 200 EnableFeatureA = abap_true ) ) FAILED DATA(failed) REPORTED DATA(reported).
" SpeichernCOMMIT ENTITIES.Helper-Klasse für einfachen Zugriff
CLASS zcl_app_settings DEFINITION PUBLIC FINAL CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS: get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_app_settings,
get_max_items RETURNING VALUE(rv_value) TYPE i,
get_default_currency RETURNING VALUE(rv_value) TYPE waers,
is_feature_a_enabled RETURNING VALUE(rv_enabled) TYPE abap_bool.
PRIVATE SECTION. CLASS-DATA: go_instance TYPE REF TO zcl_app_settings. DATA: ms_settings TYPE zi_appsettings.
METHODS constructor. METHODS load_settings.ENDCLASS.
CLASS zcl_app_settings IMPLEMENTATION.
METHOD get_instance. IF go_instance IS NOT BOUND. go_instance = NEW #( ). ENDIF. ro_instance = go_instance. ENDMETHOD.
METHOD constructor. load_settings( ). ENDMETHOD.
METHOD load_settings. READ ENTITIES OF zi_appsettings ENTITY Settings ALL FIELDS WITH VALUE #( ( SingletonKey = 'X' ) ) RESULT DATA(lt_settings).
IF lt_settings IS NOT INITIAL. ms_settings = lt_settings[ 1 ]. ENDIF. ENDMETHOD.
METHOD get_max_items. rv_value = get_instance( )->ms_settings-MaxItems. ENDMETHOD.
METHOD get_default_currency. rv_value = get_instance( )->ms_settings-DefaultCurrency. ENDMETHOD.
METHOD is_feature_a_enabled. rv_enabled = get_instance( )->ms_settings-EnableFeatureA. ENDMETHOD.
ENDCLASS.Singleton mit Parent-Entity
Ein Singleton kann auch als Child eines anderen BO existieren:
define behavior for ZI_Project alias Project{ create; update; delete;
// Jedes Projekt hat genau eine Settings-Instanz association _ProjectSettings { create; }}
define behavior for ZI_ProjectSettings alias ProjectSettingslock dependent by _Projectauthorization dependent by _Project{ // Singleton pro Parent singleton;
update; internal create;
association _Project;
field ( readonly ) ProjectID;}Best Practices
| Empfehlung | Begründung |
|---|---|
| Fester Schlüsselwert verwenden | Einfacher Zugriff ohne Suche |
internal create nutzen | Automatische Initialisierung ermöglichen |
| Helper-Klasse bereitstellen | Vereinfachter Zugriff aus ABAP-Code |
| Caching implementieren | Performance bei häufigem Zugriff |
| Berechtigungen prüfen | Nicht jeder sollte Einstellungen ändern dürfen |
| Draft für komplexe Settings | Bessere UX bei vielen Einstellungen |
Zusammenfassung
Das Singleton Pattern in RAP ist die ideale Lösung für:
- Globale Konfigurationen die zentral verwaltet werden
- Feature Flags zur Steuerung von Anwendungsverhalten
- Systemparameter die nur ein Mal existieren dürfen
Mit dem Keyword static singleton in der Behavior Definition und internal create für die automatische Initialisierung ist die Implementierung straightforward.
Weiterführende Themen
- RAP Grundlagen - Basis-Konzepte von RAP
- Draft Handling in RAP - Zwischenspeicherung verstehen
- Feature Control & Side Effects - Dynamische UI-Steuerung