Introduction

In this blog post you will read about a way to integrate your existing data models into BOPF. After some basics there will be an example of a data access class for a BO consisting of two tables containing data for a header and items.

What is the purpose of a data access class?

When moving existing developments to BOPF you will surely face the fact that you already have some data in application tables and that there will be numerous development objects accessing this database table. Next challenge will be that the key of that table will not match the requirements of  tables created by BOPF. Because of other developments referencing your old table and the data within it you cannot migrate to a database table created by BOPF. To solve this issue you can implement a data access class building the bridge between the framework and the database.

Preliminary thoughts and consequences

Before starting to implement let’s think about some aspects that may influence your implementation.

Refactoring

If you look at the structure of your database table, is there something you may do different if you could design it from scratch? If your answer is yes it’s the perfect starting point to do so.  The data access class is a kind of abstraction layer so you are free to make the necessary changes. A typical thing you should change is the admin data. There is a good reuse function within BOPF saving efforts of implementation. If you are not taking the chance now you will never do it.

There are only two reasons why you may not refactor the structure. That’s if you are just playing around with the framework or if you are sure you will never migrate to BOPF generated table layout. In these cases you would remain a higher complexity (due to mapping)  forever.

Remaining direct references to the table

You should only stick to your old tables if BOPF is not the only development component accessing the data. If only BOPF will access the data you should always use the generated tables to get the max of the framework. Be careful with the remaining references to the table. It those are inserting, modifying or deleting entries directly be sure to prevent concurrent access leading to inconsistencies. Consider those references within your migration strategy to switch to the BOPF API  with a higher priority than read accesses.

Transient vs. persistent GUIDs

The framework always uses GUIDs to identify the entities. You have the choice to create these guids only during runtime or to add them to your existing database table. If you are just playing around with the framework to gain some experience it is okay to use transient guids. Mapping transient guids during runtime costs time and is resource consuming for mass activities. Prefer to use persistent guids in your database table. It’s just a one time effort to fill the new fields.

Business logic

The purpose of the data access class is to mediate between the database and the framework and to adapt the database structure to the node structure of your BOPF BO. As a result you should never ever put any business logic into this class. Errors occurring within this layer (normally database errors) will surely lead to an abort of the current transaction.

HANA Optimization

When using your own implementation for the access of the database you are also responsible for optimizing your coding for the HANA platform. The framework cannot use it’s standard features for HANA optimization.

Class for the BO or for the node

You can choose to implement a data access class that provides the data for all nodes of the BO or for one node of the BO. In my opinion it depends on your decision to use transient or persistent GUIDs. When using persistent GUIDs it will be a good choice to create one class per node, but if you are using transient GUIDs you will need access to the internal mapping table of at least the parent node. In this case I would prefer using a single class for the complete BO.

Implementation

Before we are stating to implement the data access class (DAC) you should be familiar with the BO Builder for Experts (transaction BOBX).  To get familiar with this transaction please use the getting started guide created by Thea Hillenbrand.

Data model

The data model is an easy one. We have the table ZDAC_HEAD containing header data that will be the root node and we have the table ZDAC_ITEM that contains the item data.

data model.png

The tables can look like this:

usage screenshot
header table /wp-content/uploads/2014/08/table_zdac_head_511797.png
item table /wp-content/uploads/2014/08/table_zdac_item_511798.png

The BOPF model will be that easy too. It will look like this.

bo structure.png

Within this BO you will reference the following dictionary objects:

usage screenshot
persistent structure for node ROOT /wp-content/uploads/2014/08/zdac_s_root_d_511799.png
persistent structure for node ITEM /wp-content/uploads/2014/08/zdac_s_item_d_511800.png
alternative key for node ROOT /wp-content/uploads/2014/08/zdac_s_head_altkey_511801.png
table type to alternative key /wp-content/uploads/2014/08/zdac_t_head_altkey_511802.png

Creating the data access class

An attachment to this blog contains the complete source of the data access class. The sample BO will use transient guids so it will contain optional coding for this mapping. To be able to use a data access class you have to switch to the extended mode of the transaction BOBX.

extended mode.png

As a result you will see a new field for the data access class.

root node.png

As a naming convention I would recommend ZCL_<name of BO>[_<node>]_DAC. To create the class just use the double click. The system will detect automatically if the class needs to be created.

The system will create a class with the two interfaces:

  • /BOBF/IF_LIB_DATA_ACCESS (Tag Interface Data Access Class)
  • /BOBF/IF_BUF_DATA_ACCESS (Interface for Database Access)

Base class /BOBF/CL_DAC_IMPLEMENTATION

In my example I will inherit from class /BOBF/CL_DAC_IMPLEMENTATION. This class provides a ready-to-use implementation of the interface method GET_INSTANCE. This method is a static method. When using this class as your base class be sure that the implementation suites your requirements. Static methods cannot be redefined. This standard implementation will return an instance per node.

Conversion Methods

Before implementing the interface methods I would recommend to create methods to handle the conversion between the persistent structure of your BO and the database table.

conversion method.png

They will be used in the methods to read and write the BO data.

Query

In this method you will perform a select on the database to fetch the keys of the records that match the selection criteria. You can also execute authority checks for the selected data.  As a result you will return a table with keys only. The detailed data will be fetched by the read method. The steps to proceed are:

  • Map selection criteria to range tables
  • Select keys from database
  • Optional: build transient guids and fill mapping table
  • Optional: perform authority checks

Read

In this method you will read the detailed data for a list of keys. If you are implementing a data access class to serve for several nodes you should delegate the call to specialized methods for each node. The steps to proceed are:

  • Optional: map transient guid to database key
  • Prepare the buffer line (key fields)
  • Select record from database
  • Create data reference for the persistent structure
  • Call conversion method to transform database structure to the persistent structure

Read by Attribute

If you have specified an alternative key for your node, this method will be called to return the keys for those records in the exporting parameter ET_RESULT. In addition you can return the detailed data for those records in the export parameter ET_DATA. If the data table is empty the read method will be called later on demand. The steps to proceed are:

  • Select records from database
  • Optional: build transient guids and fill mapping table
  • Optional: read detailed data for the records
  • Return table with keys

Read Composition

This method will be called for the target node to resolve the association between a parent node and its children. You will return the child entries. To achieve this you have to proceed as follows:

  • Optional: map transient guid to database key for the parent node
  • Select keys for child records
  • Optional: build transient guids and fill mapping table for child node
  • Return key information
  • Optional: read and return detailed data

If you do not read the detailed data here they will be fetched on demand by the read method.

Write

This method is responsible for updating the database records. Like in the read method you should delegate to specialized methods for each node if you are having one class for the complete BO. Each record has contains information about the change mode. There are numerous change modes within the framework. You should take care of the following change modes within this method:

  • C = create
  • U = update
  • D = delete

If there will be old coding that can change database records as well you should take special care for data consistency. If only BOPF will have write access to these tables the framework will care for data consistency automatically. But it cannot control the world outside the framework. In the attached source code of the data access class you will find hints about extended data protection.

If the write access to the database fails you should raise an exception of the class /BOBF/CX_DAC. It already contains all you will need in this method. Please keep in mind that this exception will abort your transaction, but it’s better than having inconsistencies in your database.

Summary

Exposing your existing database tables to the framework with a data access class is easy, but it is just the starting point to get a complete BO. After those basics you will have to create your actions, determinations and validations to get a fully functional BO. In another blog post I will write about strategies on how to move your existing development to BOPF.

To report this post you need to login first.

16 Comments

You must be Logged on to comment or reply to a post.

  1. Mehmet Metin

    Hi Kai,

    Great blog! Thanks for sharing.

    This weekend I was attempting to build a custom BO to be used within the Treasury module (needed for a FPM (FBI style) that I was creating). When I got stuck at one point, I was looking for more information. It was perfectly timed when I suddenly saw your blog being published just 20 minutes before I was searching 🙂 .

    Unfortunately, I still got stuck after reading the blog knowing that something basic is missing. Today, after the coffee break of course, I discovered my typo and now it runs like a bopfsled (bad joke).

    It’s a pity that in this scenario not the complete BOPF framework can be used, especially some challenges need te be overcome with respect to writing data, but there are some workarounds possible.

    Mehmet Metin

    (0) 
  2. Ihar Partuhalau

    Hi,

    I applied similar logic in Data Access Class to update my own tables and found that records no longer saved in Database table on the root node(with keys). Is it correct behavior? If I remove this data access class I have records saved correctly in Database Table on the root node… I’m bit worried of this “side effect”, because I’m not sure how queries like “SELECT_ALL” will be working now, by default the logic for this query is sitting in /BOBF/CL* class and they look for database table on the node level

    Thank you,

    Ihar

    (0) 
    1. Kai Mindermann Post author

      Hello Ihar,

      using a data access class is a substitute to the BOPF generated database access. Therefore the generated table as well as the default logic for queries working with the generated database tables will not be used by the framework.

      What is your requirement to use both the BOPF generated table and your own table?

      Regards,

      Kai

      (0) 
      1. Ihar Partuhalau

        Hi Kai,

        I’m mistakenly thought I needed to update generated database tables to get all kind of keys (root_key, node_key, parent_key) . I used generated tables as the source to search for  in my custom Query/Action class implementations – I believe I can replace them with my own tables then.

        I still not quite sure how I am going to deal with delegated node without all these kind of keys(to get associated table).

        Thank you,

        Ihar

        (0) 
      2. Pavan Bhamidipati

        Hi Kai,

          Thanks for the information and well presented. However, regarding your comment

        using a data access class is a substitute to the BOPF generated database access. Therefore the generated table as well as the default logic for queries working with the generated database tables will not be used by the framework.

        1. Do you mean the custom DAC class should update the BOPF generated database table along with Custom table.?

        2. The non BOPF generated table is a standard ECC table where I cannot use Guids, We are looking to store the data in both the tables. BOPF DB table as well as Standard ECC tables.

        Looking for your inputs.

        (0) 
        1. Kai Mindermann Post author

          Hello Pavan,

          when using a custom DAC you will not have the BOPF generated tables. The usage of a DAC is intended for redesigns of older custom BOs and for existing tables you can read and write to directly.


          When dealing with standard ECC tables there is the problem that at least you should not write into them directly. If you use an API for the ECC table within the DAC you have to be aware of error handling. Errors within a DAC cannot be handled within an application. So all semantic checks would have to be copied into your BO as well.

          If the ECC table is not a root node you may think of using transient fields. Your BO table can be the link to the ECC table containing the generated GUID and the key fields of the ECC table. A determination can load the data from database and an action can save the data using the API provided by SAP for the standard ECC table (hope there is one 😉 ). In that case you would not have to use DAC.

          Regards,

          Kai

          (0) 
  3. Jerry Liang

    Hi Kai,

    It’s a very useful post and thanks for sharing.

    I tried to implement the example class for my BO and I got a problem when I wanted to create a new node instance. I created a form guibb and  added all feld into it. When I run it, it seems that, the input function of this feld is closed off. I can’t input values.

    I have checked feeder classe, the import parameter iv_edit_mode of method get_data has seteed as ‘E’. Could you help me out?

    Thanks in advance.

    Yinxiao

    (0) 
    1. Kai Mindermann Post author

      Hi Yinxiao,

      I experienced this behaviour in FBI if the reference to the BO or node is not set. Did you check if it works in transaction BOBT (Test tool)? If yes, it’s a configuration issue in BOPF.

      If this is your root node, please check if the settings according to scenario 2 of the blog post FPM Navigation: Parameter transfer with FBI are set and the bootstrap component ist configured correctly.

      Regards,

      Kai

      (0) 
  4. Aliaksandr Shchurko

    Thanks a lot. But usually you also should think about other lock objects that related to these tables and some APIs that already exist for these tables. In more complex scenarios I think that we should buffer class like in BAL LOG implementation. But thank a lot. Once I used similar approach.

    (0) 
    1. Tilmann David Kopp

      Hi Siva,

      there are two options:

      1. You create an action validation configured to create/update/delete and acquire the lock on the existing persistency there. If it fails, you set ET_FAILED_KEY to prevent the modification. However action validations configured that way do not prevent modifications done from inside of the BO (like for instance out of an action or determination implementation). Thus you have to reuse your locking logic in the CHECK method of all your internal entities.

      2.You switch the BOPF’s locking class implementation to an own implementation class. You can inherit from the existing /BOBF/CL_LIB_A_LOCK and add your specific locking logic.

      Best regards

      Tilmann

      (0) 
  5. SivaShankar Prakasam

    Hi Tilmann

         Thanks for your information.

         I have one more question. how the BOPF frame work knows the FPM UI  is in EDIT mode or DISPLAY mode.

    Thanks

    Siva

    (0) 
    1. Tilmann David Kopp

      Hello Siva,

      the BOPF is not aware of its consumers (e.g. FPM) and their state. The consumer has to invoke the BOPF to modify, explicit lock instances, and to ask for backend fieldcontrol information.

      Best regards

      Tilmann

      (0) 
  6. Jakob Mainka

    somehow i finally managed to get the BO running in general,

    but when I query “SELECT_ALL” only the root node gets fetched.

    Could anyone tell me what I might have missed?

    Method QUERY gets executed once -> fetches 6 keys

    Method READ gets executed 6 times -> but IT_NODE always has just 1 entry

    Maybe i missed something in the BO config, but no matter what i do, READ_COMPOSITION is not beeing called…

    (0) 

Leave a Reply