This is a test message to test the length of the message box.
Login
ABAP OO Data Access Object
Created by Software-Heroes

ABAP OO - Data Access Object (DAO)

In this article we take a look at the DAOs, what you can do with them and how they support you.

Data Access Objects or DAOs should make our lives a little easier, especially when we look at the decoupling from the database. In the following sections of the article, we will show you how to set up such an object, how you can use it and what advantages you gain with it.

 

Scenario

If you implement access to data in your source code, you usually do this directly, as you can influence your SELECT here and have the data available for the restriction. This is no problem for a small report with static data. But what happens in the next report if you want to access the same database? Right, you probably had to implement an access that might look a little different, but uses the same data.

Imagine you now have to migrate these various reports to a new system and the tables there may only exist in a different form or, in the worst case, you have to read the data from a third system via RFC and make it available.

At this point you will be faced with the challenge of swapping access to the data at every point and implementing new access. This also affects not just one object, but maybe a few more reports and classes where you access the same data.

 

DAO

A Data Access Object or DAO for short can be used at this point. At implementation time, the DAO provides a clean interface that provides the data and in most cases is responsible for accessing a data object. In our example scenario, the DAO covers exactly one table that we want to make available to various applications.

 

Structure

How is such a DAO actually structured? Here you have a lot of freedom to shape the class according to your wishes. However, you should take an interface as a basis, which also guarantees testability if you work directly with the interface and not the actual implementation. Here is an example implementation of such an interface:

INTERFACE zif_bs_demo_account_dao PUBLIC.
  TYPES:
    ts_data         TYPE zbs_dy_account,
    tt_data         TYPE STANDARD TABLE OF ts_data WITH EMPTY KEY,

    tt_r_identifier TYPE RANGE OF zbs_dy_account-identifier,
    tt_r_currency   TYPE RANGE OF zbs_dy_account-currency.

  METHODS:
    read
      IMPORTING
                id_identifier    TYPE ts_data-identifier
      RETURNING VALUE(rs_result) TYPE ts_data,

    read_query
      IMPORTING
                it_r_identifier  TYPE tt_r_identifier OPTIONAL
                it_r_currency    TYPE tt_r_currency OPTIONAL
      RETURNING VALUE(rt_result) TYPE tt_data,

    has_currency
      IMPORTING
                id_currency      TYPE ts_data-currency
      RETURNING VALUE(rd_result) TYPE abap_bool,

    create
      IMPORTING
                is_data          TYPE ts_data
      RETURNING VALUE(rd_result) TYPE abap_bool.
ENDINTERFACE.

 

The name of the interface already refers to the table or the context, so the actual method names can be very short. With the names we like to orientate ourselves on the CRUD operations (Create, Read, Update and Delete) that are possible on such an object. The implementation of further help methods is possible at any time. To do this, we create a class that implements the interface. The individual methods could therefore look like this:

CLASS zcl_bs_demo_account_dao DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES zif_bs_demo_account_dao.

  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.


CLASS zcl_bs_demo_account_dao IMPLEMENTATION.
  METHOD zif_bs_demo_account_dao~read.
    SELECT SINGLE *
      FROM zbs_dy_account
      WHERE identifier = @id_identifier
      INTO @rs_result.
  ENDMETHOD.


  METHOD zif_bs_demo_account_dao~read_query.
    SELECT *
      FROM zbs_dy_account
      WHERE identifier IN @it_r_identifier
        AND currency IN @it_r_currency
      INTO TABLE @rt_result.
  ENDMETHOD.


  METHOD zif_bs_demo_account_dao~create.
    INSERT zbs_dy_account FROM @is_data.

    rd_result = xsdbool( sy-subrc = 0 ).
  ENDMETHOD.


  METHOD zif_bs_demo_account_dao~has_currency.
    DATA(lt_data) = zif_bs_demo_account_dao~read_query(
        it_r_currency = VALUE #( ( sign = 'I' option = 'EQ' low = id_currency ) )
    ).

    rd_result = xsdbool( lt_data IS NOT INITIAL ).
  ENDMETHOD.
ENDCLASS.

 

You can see from the HAS_CURRENCY method that we can also reuse access within the DAO in order to provide simple and reusable functions without repeating ourselves when accessing the data.

 

Usage

In the next step, let's take a look at how you can use the class most effectively afterwards. You should also know that we have implemented the interface in order to decouple the functions from the actual class when used. Thus we have the possibility to exchange the DAO for another object. What does a decoupled scenario look like, plus an example of an executable class with a corresponding implementation:

CLASS zcl_bs_demo_use_dao DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

  PROTECTED SECTION.
  PRIVATE SECTION.
    DATA:
      mo_dao TYPE REF TO zif_bs_demo_account_dao.

    METHODS:
      get_dao
        RETURNING VALUE(ro_result) TYPE REF TO zif_bs_demo_account_dao.
ENDCLASS.


CLASS zcl_bs_demo_use_dao IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.

  ENDMETHOD.


  METHOD get_dao.
    IF mo_dao IS INITIAL.
      mo_dao = NEW zcl_bs_demo_account_dao( ).
    ENDIF.

    ro_result = mo_dao.
  ENDMETHOD.
ENDCLASS.

 

The reference to the DAO is located as an attribute in the class and is based on the interface. We define a method that the DAO returns to us when we request it. In this method the object is created, but only when we need it. This means that our object can already be used and can be used for access.

 

Injection

Let's take a look at a small example of how we can exchange the instance of the DAO for a different implementation. To do this, we are now developing a simple usage and calling the CREATE method twice with the same key but with different content.

DATA(lo_dao) = get_dao( ).

DATA(ld_created_1) = lo_dao->create( VALUE #( identifier = '0817' amount = '15' currency = 'EUR' ) ).
out->write( |Data 1 was created: { ld_created_1 }| ).

DATA(ld_created_2) = lo_dao->create( VALUE #( identifier = '0817' amount = '35' currency = 'USD' ) ).
out->write( |Data 2 was created: { ld_created_2 }| ).

 

Since we are trying to create the key twice, the second call of the method will return ABAP_FALSE, since the creation is not possible. Now we create a second implementation for the DAO in a local class, directly in our test class and implement the CREATE method again, this time it will always return an ABAP_TRUE, but not create a data record for it:

CLASS lcl_empty_dao DEFINITION.
  PUBLIC SECTION.
    INTERFACES: zif_bs_demo_account_dao.
ENDCLASS.

CLASS lcl_empty_dao IMPLEMENTATION.
  METHOD zif_bs_demo_account_dao~create.
    rd_result = abap_true.
  ENDMETHOD.


  METHOD zif_bs_demo_account_dao~has_currency.
  ENDMETHOD.


  METHOD zif_bs_demo_account_dao~read.
  ENDMETHOD.


  METHOD zif_bs_demo_account_dao~read_query.
  ENDMETHOD.
ENDCLASS.

 

The full example now looks like this. The output generated in the console will always display two ABAP_TRUE values and no more data records will be created in the database.

CLASS zcl_bs_demo_use_dao DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

  PROTECTED SECTION.
  PRIVATE SECTION.
    DATA:
      mo_dao TYPE REF TO zif_bs_demo_account_dao.

    METHODS:
      get_dao
        RETURNING VALUE(ro_result) TYPE REF TO zif_bs_demo_account_dao.
ENDCLASS.


CLASS zcl_bs_demo_use_dao IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    mo_dao = NEW lcl_empty_dao( ).

    DATA(lo_dao) = get_dao( ).

    DATA(ld_created_1) = lo_dao->create( VALUE #( identifier = '0818' amount = '15' currency = 'EUR' ) ).
    out->write( |Data 1 was created: { ld_created_1 }| ).

    DATA(ld_created_2) = lo_dao->create( VALUE #( identifier = '0818' amount = '35' currency = 'USD' ) ).
    out->write( |Data 2 was created: { ld_created_2 }| ).
  ENDMETHOD.


  METHOD get_dao.
    IF mo_dao IS INITIAL.
      mo_dao = NEW zcl_bs_demo_account_dao( ).
    ENDIF.

    ro_result = mo_dao.
  ENDMETHOD.
ENDCLASS.

 

This should give you a rough idea of how you can exchange the DAO at runtime and use a different implementation. The example shown is more of a fictional nature and is only intended to illustrate how you can implement the function.

 

Advantages

The use of the DAO brings some advantages for you in the development, which we would like to list again at this point:

  • Single Point of Access - An object that provides the data for a data source and can be used over and over again.
  • Expandability - Expandability with new functions, fields or logics can easily be implemented in one place.
  • Encapsulation - decoupling of the data access from the business logic and thus more stability in the calling reports and classes.
  • Data separation - which data source is behind the data provision is now completely decoupled. The data can be made available from a table, a CDS view, RFC or OData.
  • Testability - The testing application can now also simulate data access to dummy data, even if there is no test double framework (older releases).

 

Conclusion

You should give the DAO a chance and use it in your next project. The initial effort for the provision is relatively large, but you can already take the first benefit with the unit tests and at the latest with extensions and/or a migration you will recognize the advantage.


Included topics:
OOABAP OOData Access ObjectDAO
Comments (0)

ABAP OO - Method interface

Category - ABAP

What should method interfaces currently look like and how do you achieve this? In this article we will clarify the question.

11/19/2021

ABAP OO - Chaining and casting

Category - ABAP

This article is about the chaining of method and object calls, we would also like to explain the background to casting to you.

08/20/2021

ABAP OO - Constant interface

Category - ABAP

The use of constants in object-oriented programming is standard for most classes. Today we're going to show you a simple example of overarching objects.

07/16/2021

ABAP OO - Exception classes

Category - ABAP

Today we look at the different exception classes and how you can create and use your own exception classes.

07/09/2021

ABAP OO - Exception Handling

Category - ABAP

What about error handling in object-oriented programming? We want to address this question in this article.

07/02/2021