Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Creating Persistent Class to Access Multiple Tables

Former Member
0 Kudos

Hello all,

How can I create a persistent class that can access multiple tables? I have created several persistent classes for single tables but have not yet managed to successfully activate a persistent class that accesses several tables.

In the contrived example that I am working with I have two tables; ZTST_PERSK and ZTST_PERSP. The layouts of the tables are:


ZTST_PERSK
FIELD           KEY     DATA ELEMENT
MANDT           X       SYMANDT
VBELN           X       VBELN
AUART                   AUART

ZTST_PERSP
FIELD           KEY     DATA ELEMENT
MANDT           X       SYMANDT
VBELN           X       VBELN
POSNR           X       POSNR
MATNR                   MATNR

These simple tables are designed to demonstrate the most basic of parent/child relationships.

To create the persistent class I go to SE24, select the persistent class option and then from the main class editor screen click on the persistence button. Immediately the add table/structure popup window appears, I enter ZTST_PERSK and then add VBELN and AUART from the editing area to the attribute display without changing any of attribute name/visibility/etc. I then add table ZTST_PERSP and proceed to add all of it's fields from the editing area to the attribute display.

Once all the fields of both tables have been added I go back to the main class editor screen and attempt to activate the class. Upon clicking on the activate button I receive the following error:

"There is no mapping defined the the table ZTST_PERK for the key field POSNR."

Can anyone explain what this error means?

Is this the correct way to create a persistent class for multiple tables with a parent child relationship? Should the tables use GUIDs instead? (I have tried creating tables using GUIDs as the data elements for the VBELN and POSNR fields but got stuck with a different error instead.)

Thanks,

Steve.

7 REPLIES 7

Former Member
0 Kudos

Having considered this problem further, maybe it's not possible to create one persistent class that maps to several tables in a parent/child relationship. Using the scenario I described above, say for example that 1 record in ZTST_PERSK has 10 corresponding records in ZTST_PERSP. How would the persistent class represent the attributes of all ten rows in ZTST_PERSP? It would have to contain a table of objects that represent the items. Each object would have to contain all the attributes of the item and the table would also need key field that represent the business keys of the item table.

Having also read Thomas Jung's blog: <a href="/people/thomas.jung3/blog/2004/12/08/abap-persistent-classes-coding-without-sql Persistent Classes: Coding without SQL</a> I noted that he states that he had to create multiple persistent classes for the various tables of his business object and then wrote a data model class to tie them together. Is a data model class just a generic class (class type 0) that contains attributes of the type of the persistent classes you wish to combine together?

Any advice on design patterns that can be used when building data model classes would be appreciated.

Thanks,

Steve.

0 Kudos

By Data Model Class I do just mean a standard class. There is no special support in the workbench for this type of object - it is just a logical construct.

I always generate my persistent classes as a one-to-one relationship to my database tables. Order Header - VBAK - ZCL_PERS_VBAK and Order Items -VBAP - ZCL_PERS_VBAP for instance. I then create what I call a data model class - ZCL_SALES_ORDER. This class would have methods like GET_HEADER, GET_ITEMS and perhaps even GET_ORDER which would contain exporting parameters for both the header and items. Inside this data class is all the logic to interact with the persistent object. Outside application classes only ever have to deal with the data model class - it would also include all the locking logic for instance in an update transaction.

In one example, I had a large custom system. It had about 20 database tables. Each table had its own persistent object. However I only created one data model class. That data model class was then used in one Base (Abstract) Application class. That Base Application Class was then used as the Application Model for two different UI implementations (SAPGui and BSP). The Base Abstract Application class had basic application logic that was shared by both UI versions. The more specific implementation application classes had logic specific to the UI technology being used (One was actually a BSP Model Class - inheriting from CL_BSP_MODEL2 and the other had logic structured to PBO/PAI for SAPGui).

0 Kudos

Firstly, many thanks for such a speedy reply!

So to clarify using the VBAK/VBAP example to create an application:

Create a persistent class for each table in the data model.

Create a standard class that contains attributes that have the type of the persistent classes. (1)

Create the necessary methods for working with the tables; GET_HEADER, GET_ITEM, GET_ITEMS, SET_HEADER, SET_ITEM, SET_ITEMS, CREATE_OBJECT, DELETE_OBJECT, etc.

Create an application class and create methods to work with the object at the application level; therefore the application class method CREATE_ORDER will call the data model class CREATE_OBJECT. (2) (Does the application class inherit from the data model class or is the data model class used as the type of an attribute of the application class?)

Create UI class(es) that inherit from the application class and contain UI specifc code.

I'm sorry for all the questions but I find this a very interesting topic and want to know the best way to implement a test application in my own time.

Thanks,

Steve.

(1) I have visualised implementing the attributes of the data model class as dictionary structures that are table types that contain the keys of the table of the persistent class and two non key fields, one flag called (for example) UPD to show that one or more attributes of the object have been updated so the object needs to be written to the DB and the other which has the type of the persistent class. So, the table type for the VBAP attribute would have VBELN and POSNR as keys and UPD and VBAP_OBJ with type REF to ZCL_PERS_VBAP. Is this a sensible way of implementing the attributes?

(2) in your most recent reply you said that the base application class is abstract (therefore contains no code) and is used as the basis for UI implementations. In the VBAK/VBAP example doesn't that meant that application logic has to be duplicated in the UI classes? For instance, if the logic that a delivery date in VBAP cannot be earlier than todays date is required, it would have to be implemented in both SAPGUI and BSP classes.) If the base application class is not abstract this sort of logic can be implemented in the base application class and would automatically be available to the UI classes. As always, there are many ways to skin a cat!

0 Kudos

>Does the application class inherit from the data model class or is the data model class used as the type of an attribute of the application class?)

I supose either way is correct. I always make the data model class instance an attribut of the application class however. That way new instances can be created easily and you could have more than type of data model in use at once (for instance sales order and invoices).

>1) I have visualised implementing the attributes of the data model class as dictionary structures that are table types that contain the keys of the table of the persistent class and two non key fields, one flag called (for example) UPD to show that one or more attributes of the object have been updated so the object needs to be written to the DB and the other which has the type of the persistent class. So, the table type for the VBAP attribute would have VBELN and POSNR as keys and UPD and VBAP_OBJ with type REF to ZCL_PERS_VBAP. Is this a sensible way of implementing the attributes?

Yes that is nearly the same as I have done it in the past. I only treat these as private attributes however. You are able to keep track of multiple instances of your persistent object this way. However in my GET_ITEM, I never pass back the reference to ZCL_PERS_VBAP. I read the data from this object and map it into a flat structure - ZCL_PERS_VBAP becomes IVBAP of type VBAP. Or in the case of VBAP I might have a GET_ITEMS that returns the normal internal type of type VBAP. That way any external logic knows nothing about the persistent layers. This also gives you the freedom to combine fields from multiple persistent object and to do calculations at the data object level. The Application Object doesn't need to know if a field is calculated or comes from the database, nor does it need to know what table it came from. This makes it much easier to adjust your data model over time without effecting your application as much.

2>in your most recent reply you said that the base application class is abstract (therefore contains no code) and is used as the basis for UI implementations. In the VBAK/VBAP example doesn't that meant that application logic has to be duplicated in the UI classes?

No if you use an abstract class it can contain code! That is the main difference between an interface and an abstract class. You could also just create a regular class and inherit the base logic from it. I like the abstract class because it can't be instantiated on its own. The base class is useful because the logic is reusable, but it is incomplete. Without the surrounding logic that is UI specific, you wouldn't want to use this class directly. Therefore it is abstract, keeping another developer from using it. If another implementation is needed (for instance to turn it into an Enterprise Service, you would create a new class that inherits from the same base class). This also creates a clear hierarchy of how the object is used (can be quickly seen by viewing which classes inherit from the abstract base class).

0 Kudos

Thanks for another speedy reply!

I see why you use an abstract class now! It makes sense. I had confused interfaces and abstract classses.

In the data model class is there a quick way of mapping the attributes of the persistent class to a dictionary structure? I mean, does the GET_ITEM method need to consist of many lines like this:


     IVBAP-VBELN = LO_PERS_VBAP->VBELN
     IVBAP-POSNR = LO_PERS_VBAP->POSNR
     etc.

I don't think you can do a move-corresponding with object attributes, or can you? Maybe it's possible to do something with the CL_ABAP_STRUCTDESCR class? (Anything to reduce the amount of typing I have to do

The above point may seem like laziness but it would be great to incorporate something using the CL_ABAP_STRUCTDESCR class into a design pattern. In this way the database tables could be created using key fields and include structures and the low level data model class methods could use structures that include the database include structure and any additional include structures containing derived fields. This would accelerate the time taken to create data model classes.

0 Kudos

There is no good trick that I know of to map the structures. When I built a particularly large data model class once, I did a little cut and paste magic to speed up the work.

Your idea of using the RTTS is probably doable in combination with a macro or some dynamically generated coding. But if you data model has to support very high volumes of execution, you might get better performance from the hand written code.

0 Kudos

After a little R&D I've found that it's possible to do the equivalent of a move-corresponding from the attributes of an object to the similarly named elements of a structure using the following code below.

The method below has two parameters; an importing parameter 'IO_OBJECT TYPE REF TO OBJECT' and a exporting parameter 'EX_RESULT TYPE ANY'. The IO_OBJECT parameter should be an object with type reference to a persistent class object.


METHOD map_pers_to_dms.

  DATA:
    lc_method_name(30) TYPE c,

    lo_typedescr       TYPE REF TO cl_abap_structdescr,

    ptab               TYPE abap_parmbind_tab,
    ptab_line          TYPE abap_parmbind.

  FIELD-SYMBOLS:
    <ls_components> TYPE abap_compdescr,
    <lx_field>      TYPE ANY.

  lo_typedescr ?= cl_abap_typedescr=>describe_by_data( ex_result ).
  IF lo_typedescr IS NOT INITIAL.

    LOOP AT lo_typedescr->components ASSIGNING <ls_components>.
      ASSIGN COMPONENT <ls_components>-name OF STRUCTURE ex_result
        TO <lx_field>.
      IF sy-subrc = 0.
        CASE <ls_components>-name.
          WHEN 'MANDT'.
            <lx_field> = sy-mandt.
          WHEN 'OBJECT'.
            <lx_field> ?= io_object.
          WHEN OTHERS.
            CONCATENATE 'GET_' <ls_components>-name INTO lc_method_name.
            CLEAR ptab[].
            ptab_line-name = 'RESULT'.
            ptab_line-kind = cl_abap_objectdescr=>returning.
            GET REFERENCE OF <lx_field> INTO ptab_line-value.
            INSERT ptab_line INTO TABLE ptab.
            CALL METHOD io_object->(lc_method_name)
              PARAMETER-TABLE
                ptab.
        ENDCASE.
      ENDIF.
    ENDLOOP.
  ENDIF.

ENDMETHOD.

Hopefully someone will find this code useful. It can be used to map the attributes of any persistent class to the data structure that was used to generate that same persistent class.