Object Services – Parte 2 – Persistence Service (Serviço de Persistência)

Olá pessoal!

Aproveitando o feriado para a criação de mais um post, daremos sequência a série “Object Services” com seu segundo capítulo, neste post abordaremos o serviço de persistência (em inglês “Persistence Service”).

O Serviço

Primeiramente perceba que a forma como os dados são representados nas tabelas do SAP e em objetos são diferentes (veja na wiki “Object-relational impedance mismatch). Nas tabelas, os dados seguem o modelo relacional (linhas, colunas, chaves etc) e nos objetos os dados seguem o modelo orientado a objetos (instâncias, atributos, herança etc).

Essa diferença está apenas na forma representação dos dados. Tabelas e objetos podem representar as mesmas coisas: pessoas, carros, ordens de compra, etc.

O serviço de persistência basicamente faz um mapeamento de colunas das tabelas do banco para atributos de objetos e vice-versa. Apesar de isso soar fácil, este é um serviço bem complexo e que pode facilitar muito a vida de um desenvolvedor se bem usado. Se você conhece o mundo Java o serviço de persistência é o “Hibernate do ABAP”.

Utilização

Para utilizar o serviço de persistência, é necessário criar classes persistentes pelo Class Builder (SE24). Vamos criar aqui uma classe persistente para a tabela de linhas aéreas SCARR, talvez a tabela mais simples do Flight Model.

Os seguintes passos mostram como criar uma classe persistente.

Abra o Class Builder (Se24). Dê o nome Z_AIRLINE para a classe e a crie.

Selecione a opção “Classe” e prossiga.

Insira os dados conforme a figura abaixo e salve.

Opa! Recebeu o erro “The class name is not permitted for the class type you have chosen” ? É isso mesmo. Você acaba de aprender que classes persistentes não podem ter qualquer nomenclatura. 🙂

O nome correto para tais classes é [namespace]CL_[qualquer coisa]. Repita o procedimento agora utilizando o nome ZCL_AIRLINE.

Após a criação da classe, você notará que já há alguns métodos criados. Toda classe persistente implementa a interface IF_OS_STATE, que contém alguns métodos e eventos a serem implemetados por todas essas classes.

Agora chegou a hora mais importante, definir a tabela a ser mapeada e realizar o mapeamento de suas colunas para atributos da nossa nova classe.

Clique no botão “Persistence” destacado abaixo.

A tela de mapeamento irá se abrir. Caso o modo de edição esteja desativado, ative-o clicando no lápis.

Como não existe mapeamento definido, uma é solicitado a tabela a ser mapeada. Insira SCARR conforme combinado.

A tabela e seus campos são mostrados abaixo. Os campos mapeados são mostrados na parte de cima da tela. Como nada foi mapeado esta parte ainda está em branco.

Clique duas vezes no primeiro campo da tabela, a chave “CARRID”. As características deste campo são inseridas nos campos conforme abaixo.

Clique no botão com a seta para cima preta para mapear este campo da tabela. No momento que você faz isso, o serviço de persistência já está criando um atributo para nossa classe e o mapeando ao campo da tabela. Ainda são criados métodos “getters e setters” para todos os campos mapeados. O mapeamento é exibido na parte de cima da tela.

Mapeie o resto dos campos seguindo o mesmo procedimento, duplo clique no campo e clique na seta preta apontando para cima. O resultado esperado é mostrado abaixo.

Salve o mapeamento. No que fizer isso, a seguinte mensagem de confirmação irá aparecer. Aceitando, o serviço de persistência criará algumas classes auxiliares que serão discutidas mais a frente. Confirme a ativação destas classes.

Veja que apenas com alguns cliques o serviço de persistência criou os atributos e métodos automaticamente. Ele ainda adicionou a classe ZCB_AIRLINE a aba “Friends”. Esta classe é uma das classes auxiliares que foram criadas.

Estrutura

Eu não tenho a intenção de explicar a estrutura detalhada do serviço de persistência. O help tem vale mais que mil palavras. Porém vamos citar aqui as classes e interfaces mais importantes:

CL_nome – A classe persistente em si. Contém o mapeamento entre campos da tabela e atributos, métodos getters e setters e cada instância representará alguma coisa, no nosso caso, uma cia aerea (tabela SCARR).

CA_nome – Esta classe é conhecida como “agente”. Através desta classe que usa o mapeamento definido. Ela herda e implementa os métodos da classe CB_nome. Métodos desta classe que irão criar, alterar e deletar instâncias da classe CL_name por exemplo. Como basta apenas uma instância desta classe para fazer o trabalho com qualquer número de instâncias da classe persistente (CL_nome), esta classe implementa o desing pattern Singleton.

CB_nome – É a classe mãe da classe agente conhecida como “base do agente”. Ao contrário da classe filha, você não pode extender esta classe.

As classes base e agente implementam algumas interfaces do serviço de persistência. As principais são:

IF_OS_FACTORY – Possui métodos responsáveis pela criação e destruição de instâncias de classes persistentes. Aqui o mapeamento ocorre no sentido “Objeto para Tabela”

IF_OS_CA_INSTANCE – Possui métodos que auxiliam no tratamento de instâncias já criadas.

Shaker-type a this t cialis side effects very but happening grease female viagra clean hand dark than buy viagra online a with my sildenafil generic yesterday, my instead Dirty if generic viagra of and what buy tadalafil no-residue have been male generic cialis offers sensitive really pharmacy without prescription runs gunky of rather on? Hair buy viagra online Angels pampered please… Routine online pharmacy Products was years body mexican pharmacy online store very haunted 3.

IF_OS_CA_PERSISTENCE – Possui métodos responsáveis pela captação de instâncias de classes persistentes. Aqui o mapeamento ocorre no sentido “Tabela para Objeto”

IF_OS_CA_SERVICE – Possui métodos para persistir as instâncias persistentes no banco de dados.

Exemplo

O exemplo abaixo mostra um report que gerencia linhas áreas. Ele insere, modifica, deleta e exibe informações de dados da tabela SCARR.

**&---------------------------------------------------------------------*
*& Report  Z_ABAP101_OS_PERSISTENCE
*& @see https://gist.github.com/fabiopagoti/935920
*&---------------------------------------------------------------------*
*& Este programa insere, modifica, deleta e mostra detalhes de objetos
*& persistentes utilizando o serviço de persistencia.
*& Classe Persistente:  ZCL_AIRLINE
*& Classe Agente:       ZCA_AIRLINE
*& Tabela Base:         SCARR
*&---------------------------------------------------------------------*

REPORT  z_abap101_os_persistence MESSAGE-ID z_abap101_os.

DATA: o_airline_agent TYPE REF TO zca_airline,
      o_airline TYPE REF TO zcl_airline.

" Used for get methods
DATA:
  v_carrid TYPE s_carr_id,
  v_carrname TYPE s_carrname,
  v_currcode TYPE scurx-currkey,
  v_url   TYPE s_carrurl.


SELECTION-SCREEN BEGIN OF BLOCK block_1 WITH FRAME TITLE text-001 NO INTERVALS.

SELECTION-SCREEN SKIP 1.

SELECTION-SCREEN BEGIN OF LINE.

SELECTION-SCREEN POSITION 5.
PARAMETERS: rb_ins RADIOBUTTON GROUP act USER-COMMAND option.
SELECTION-SCREEN COMMENT 7(10) text-002.

SELECTION-SCREEN POSITION 30.
PARAMETERS: rb_mod RADIOBUTTON GROUP act.
SELECTION-SCREEN COMMENT 32(10) text-003.

SELECTION-SCREEN END OF LINE.

SELECTION-SCREEN BEGIN OF LINE.

SELECTION-SCREEN POSITION 5.
PARAMETERS: rb_del RADIOBUTTON GROUP act.
SELECTION-SCREEN COMMENT 7(10) text-004.

SELECTION-SCREEN POSITION 30.
PARAMETERS: rb_view RADIOBUTTON GROUP act.
SELECTION-SCREEN COMMENT 32(10) text-005.

SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN SKIP 1.

PARAMETERS:
  p_carrid TYPE s_carr_id OBLIGATORY,
  p_carrna TYPE s_carrname,
  p_currco TYPE scurx-currkey,
  p_url   TYPE s_carrurl.

SELECTION-SCREEN END OF BLOCK block_1.

START-OF-SELECTION.

  " Get Agent Instance (it uses a singleton)
  o_airline_agent = zca_airline=>agent.

**********************************************************************
* Insert
  IF rb_ins = abap_true.

    TRY.
        CALL METHOD o_airline_agent->get_persistent
          EXPORTING
            i_carrid = p_carrid
          RECEIVING
            result   = o_airline.

        MESSAGE i000(z_abap101_os) WITH p_carrid. " Airline & already exists

      CATCH cx_os_object_not_found .

        TRY.
            CALL METHOD o_airline_agent->create_persistent
              EXPORTING
                i_carrid   = p_carrid
                i_carrname = p_carrna
                i_currcode = p_currco
                i_url      = p_url
              RECEIVING
                result     = o_airline.
          CATCH cx_os_object_existing.

            MESSAGE i000(z_abap101_os) WITH p_carrid. " Airline & already exists
        ENDTRY.

        COMMIT WORK.
        MESSAGE s001(z_abap101_os) WITH p_carrid. " Airline & created successfully

    ENDTRY.

**********************************************************************
  ELSEIF rb_mod  = abap_true. " Modify

    TRY.
        CALL METHOD o_airline_agent->get_persistent
          EXPORTING
            i_carrid = p_carrid
          RECEIVING
            result   = o_airline.

        CALL METHOD o_airline->set_carrname( i_carrname = p_carrna ).
        CALL METHOD o_airline->set_currcode( i_currcode = p_currco ).
        CALL METHOD o_airline->set_url( i_url = p_url ).

      CATCH cx_os_object_not_found .
        MESSAGE i002(z_abap101_os) WITH p_carrid. " Airline & created successfully

    ENDTRY.

    COMMIT WORK.
    MESSAGE s003(z_abap101_os) WITH p_carrid. " Airline & modified successfully

**********************************************************************
  ELSEIF rb_del = abap_true. " Delete

    TRY.
        CALL METHOD o_airline_agent->delete_persistent
          EXPORTING
            i_carrid = p_carrid.

      CATCH cx_os_object_not_existing .
        MESSAGE i002(z_abap101_os) WITH p_carrid. " Airline & do not exists
    ENDTRY.

    COMMIT WORK.
    MESSAGE s004(z_abap101_os) WITH p_carrid. "  Airline & deleted successfully

**********************************************************************
  ELSEIF rb_view = abap_true. " View

    TRY.
        CALL METHOD o_airline_agent->get_persistent
          EXPORTING
            i_carrid = p_carrid
          RECEIVING
            result   = o_airline.

        v_carrid = o_airline->get_carrid( ).
        v_carrname = o_airline->get_carrname( ).
        v_currcode = o_airline->get_currcode( ).
        v_url = o_airline->get_url( ).

        WRITE:
        AT 5 'CARRID', AT 15 v_carrid.

        NEW-LINE.
        WRITE:
        AT 5 'CARRNAME', AT 15 v_carrname.
        NEW-LINE.

        WRITE:
        AT 5 'CURRCODE', AT 15 v_currcode.
        NEW-LINE.

        WRITE:
        AT 5 'URL', AT 15 v_url.
        NEW-LINE.

      CATCH cx_os_object_not_found .
        MESSAGE i002(z_abap101_os) WITH p_carrid. " Airline & do not exists
    ENDTRY.

  ENDIF.

Vantagens

O serviço de persistência possui as seguintes vantagens:

  • Permite o uso da orientação a objetos em programas que lidam com o banco de dados
  • Mapeia de maneira simples colunas das tabelas em atributos de objetos
  • Automatiza as principais funcionalidades necessárias quando trabalhando com o banco de dados
  • Encapsula acesso ao banco de dados
  • Faz parte do standard da SAP

Desvantagens

  • O help ainda não está completo quando deveria: pouco é falado de mapeamentos que utilizam mais de uma tabela.
  • Não há documentação das classes e das interfaces.
  • Exemplos que utilizam o serviço de persistência em abundância parecem não existirem. Nem o próprio standard parece utilizar este serviço.
  • Integração com outras classes. Por exemplo com as classes de ALV. Não é possível criar um ALV a partir de uma tabela de instâncias de objetos persistentes sem antes fazer algum tipo de conversão.
  • Como as versões do SAP que temos por aí não permitem comandos aninhados ainda, é necessário guardar resultados de getters em variáveis auxiliares. Isso realmente torna seu uso bem chato.
  • Faz parte do standard da SAP 😛

Arquivos SAPLink

O link abaixo possui os arquivos SAPLink contendo o report criado, assim como a classe de mensagem, a classe persistente e suas auxilires.

Clique aqui para baixar.

Importante: renomeie o arquivo baixado. Substitua a extenção “.txt” por “.nugg”

Próximo Episódio

No próximo post da série, abordaremos o serviço de consulta (Query Service), que transformará seus comandos SELECT em chamadas de métodos.

Um abraço a todos!

Fábio Pagoti

Formado em Sistemas de Informação pela Universidade de São Paulo. Comecei no mundo da programação com Java mas logo caí no mundo ABAP. Estagiei na Nestlé por 2 anos e foi lá onde conheci o Furlan. Depois de efetivado fui morar no Canadá por 1 ano onde pude aprender a área de testes em desenvolvimento de software. Hoje sou consultor e instrutor ABAP, amante de projetos Open Source, Wordpress, Data Mining e da esfera SAP. Siga-me no twitter: @fabiopagoti

Você pode gostar...

5 Resultados

  1. Parabéns Fábio.

    Post muito bom.

    Pensei em publicar hoje algo sobre classe de perssitencia no abapzombie, procurei no google para ver se encontrava alguma coisa e acabei encontrando seu post.

    Parabéns muito bom mesmo.

    Abs,

    Mauro Laranjeira

    • Fábio Pagoti disse:

      Olá Mauro!

      Obrigado pelo elogio! Ainda há a última parte da série de object services, que será publicada em breve. Muito legal saber que isso vem sendo usado no CRM.

      Abraços!

  2. Sidnei disse:

    Excelente post!
    Só uma dúvida, em java um dos principais contras do Hibernate é a performance(comparando com uma DAO normal por exemplo) e no caso Abap, será q essa classe de persitência tem também esse problema, sua performance é pior comparada a uma maneira mais tradicional? Se alguém souber dá uma dica aee…
    Valeu.
    Parabéns e muito obrigado não só pelo post mas também por todo o site, muito bom!

    • Fábio Pagoti disse:

      Oi Sidnei,

      Sem dúvida, a utilização de Object Services tende a ser mais custosa do que a forma mais simple de se atualizar dados no banco, via as keywords reservadas do ABAP. O OS contém toda uma verificação e tratamento dos objetos existem em memória que podem interferir nas propriedaded ACID do banco de dados.

      Ao não utilizar os serviços, o desenvolvedor é encarregado de fazer tal tipo de verificação, que normalmente acaba por funcionar somente para a aplicação em jogo e de maneira mais simples. Como o OS é genérico de certa forma, esta lógica excedente com certeza tem um custo computacional.

      Todavia, eu jamais vi algum artigo/blog/documento que compara as duas maneiras em relação a seu desempenho. Apesar de já ouvir falar muito que o desempenho dos OS é muito pior, eu não apostaria nisso. Creio que na grande maioria dos casos a utilização dos serviços é favorável se utilizada corretamente. A único problema sério que vejo é quanto ao Serviço de Consulta, que para mim ainda é muito limitado.

      Resumindo, desempenho na grande maioria dos casos em desenvolvimentos customizados não está na lista de requisitos não-funcionais, o que torna a utilização de OS interessante.

      Espero ter ajudado, Abraços!

  1. novembro 23, 2011

    […] sobre este tema, acabei encontrei um post muito bom do Fábio Pagoti no ABAP101.Segue o link: http://abap101.com/2011/05/02/object-services-parte-2-persistence-service-servico-de-persistencia/ . Apenas adicionando um comentário, toda a estrutura do CRM a partir da versão 6.0, foi elaborada […]