Object Services – Parte 3 – Query Service (Serviço de Consulta)

Olá Leitor!

Este é o terceiro post da série “Object Services”, hoje falaremos sobre o serviço de consulta (em inglês “Query Service”).

O Serviço

No ABAP, diferentemente de outras linguagens, lhe permite buscar dados do banco de uma maneira muito simples e padronizada. ABAPers não se importam com conexões, strings de consulta, adaptadores etc. Na verdade nem precisamos saber que diabos são essas coisas. Como as aplicações em ABAP quase sempre utilizam dados de tabelas nada melhor do que facilitar a sua busca.

Pois bem, em outras linguagens que não possuem essa característica de extração de dados do banco facilitada é comum que se crie classes responsáveis para tal. Há classes que tratam a conexão da aplicação com o banco, outras são responsáveis por extrair dados de tabelas, outras facilitam a contrução das cláusulas de seleção. Uma vez que a extração de dados está encapsulada em classes específicas para tal finalidade, a adoção do padrão MVC torna-se mais simples.

Pelo propósito e histórico de evolução do ABAP, um programa pode virar uma grande mistura de comandos de extração de dados, lógica de aplicação e saída para o usuário. Logo, caso haja qualquer tipo de mudança na regra de negócio, desde a seleção de mais dados de uma ordem, passando pela inclusão de um cálculo de imposto diferenciado até a trivialidade de imprimir um campo de total em amarelo ao invés de azul significam um trabalho sobre o mesmo código. Claro que se tudo fosse bem dividido, o trabalho para indentificar e corrigir bugs e extender uma funcionalidade seria muito mais fácil. Por isso o serviço de consulta se torna importante: para encapsular a seleção de dados.

Utilização

A utilização do serviço de consulta está muito vinculada com o serviço de persistência que vimos no último post desta série. Veremos que objetos de ambos os serviços são muitas vezes utilizados juntos. O exemplo que mostraremos usa duas classes persistentes, a ZCL_AIRLINE, que mapeia os campos da tabela SCARR e a classe ZCL_FLIGHT_SHEDULE, que mapeia os campos da tabela SCARR. Se você não sabe criar classes persistentes, veja o último post da série.

Estrutura

Para se usar do serviço de consulta, as classes e interfaces abaixo são mais do que tudo que você precisa saber:

CL_OS_QUERY – Representa uma consulta a ser realizada. Compare cada instância desta classe como um comando SELECT encapsulado. As interfaces que esta classe implementa acabam sendo mais importantes do que ela mesma.

CL_OS_QUERY_MANAGER – Classe que cria (ou gerencia) as suas consultas.

Como somente é necessário um único gerenciador para qualquer número de consultas, esta classe implementa o design pattern “Singleton”.

 

IF_OS_QUERY_MANAGER – Interace implementada pelo gerenciador de consultas.

IF_OS_QUERY – Uma consulta basicamente deve ter uma condição (filtro), os parâmetros utilizados e um critério de ordenação. É exatamente isso que esta interface contém: assinaturas de métodos para tratar esses três itens.

IF_OS_QUERY_FILTER_EXPR – Representa uma expressão de consulta

IF_OS_QUERY_EXPR_FACTORY – Representa algo que constrói expressões de consulta

IF_OS_QUERY_ORDERING_EXPR – Representa a expreção de ordenação

IF_OS_QUERY_PARAMETERS_EXPR – Representa os parâmetros a serem utilizados na expressão.

Exemplo

O exemplo abaixo é um relatório simples de vôos. Na tela de seleção você insere uma cia aérea e utilizando o serviço de consulta, informações adicionais (moeda e website) são mostrados. Essas informações são capturadas usando a classe persistente que criamos no último post desta série.

Há também dois checkboxes nos quais é possível indicar vôos entre um mesmo país ou entre países diferentes. Ao executar o programa, informações dos vôos que se enquadram nos parâmetros inseridos (cia aérea + vôos nacionais e internacionais) são mostrados. Essas informações são buscadas usando uma classe persistente chamada “ZCL_FLIGHT_SHEDULE”, que mapeia todos os campos da tabela SPFLI. Você pode criar essa classe da mesma maneira dita no último post desta série citado anteriormente.

**&---------------------------------------------------------------------*
*& Report  Z_ABAP101_OS_PERSISTENCE
*&
*&---------------------------------------------------------------------*
*& Este programa busca os horários de voos de uma dada compania area
*& utilizando o serviço de consulta (query service)
*& Classes Persistente:  ZCL_AIRLINE e ZCL_FLIGHT_SCHEDULE
*& Classes Agente:       ZCA_AIRLINE e ZCA_FLIGHT_SCHEDULE
*& Tabelas Base:         SCARR e SPFLI
*&---------------------------------------------------------------------*

REPORT  z_abap101_os_query MESSAGE-ID z_abap101_os.

" Agentes
DATA: o_airline_agent TYPE REF TO zca_airline,
      o_flight_schedule_agent TYPE REF TO zca_flight_schedule.

" Objetos persistentes
DATA: o_airline TYPE REF TO zcl_airline,
      o_flight_schedule TYPE REF TO zcl_flight_schedule.

" Objects of Query Service
DATA: o_query_manager TYPE REF TO cl_os_query_manager,
      o_query TYPE REF TO if_os_query,
      o_ref TYPE osref,
      i_o_ref TYPE osreftab.

" Auxiliar for getters
DATA: v_connid    TYPE spfli-connid,
      v_countryfr TYPE spfli-countryfr,
      v_countryto TYPE spfli-countryto.

**********************************************************************
* Selection Screen
**********************************************************************
SELECTION-SCREEN BEGIN OF BLOCK block_1 WITH FRAME TITLE text-001.

SELECTION-SCREEN SKIP 1.

SELECTION-SCREEN BEGIN OF LINE.

SELECTION-SCREEN POSITION 1.
SELECTION-SCREEN COMMENT 2(7) text-002.
SELECTION-SCREEN POSITION 9.
PARAMETERS: lb_airli TYPE s_carr_id AS LISTBOX VISIBLE LENGTH 25 USER-COMMAND listbox.
SELECTION-SCREEN COMMENT 35(8) text-003. " Currency
SELECTION-SCREEN COMMENT 44(30) v_currco.

SELECTION-SCREEN END OF LINE.

SELECTION-SCREEN BEGIN OF LINE.

SELECTION-SCREEN COMMENT 35(8) text-004. " Website
SELECTION-SCREEN COMMENT 44(30) v_url.

SELECTION-SCREEN END OF LINE.

SELECTION-SCREEN SKIP 1.

SELECTION-SCREEN BEGIN OF LINE.
SELECTION-SCREEN POSITION 1.
PARAMETERS: ch_inter AS CHECKBOX DEFAULT abap_true USER-COMMAND dest_internal.
SELECTION-SCREEN COMMENT 3(8) text-005. " Internal Flights
SELECTION-SCREEN POSITION 12.
SELECTION-SCREEN COMMENT 13(4) v_inter. " Number of Internal Flights

SELECTION-SCREEN END OF LINE.

SELECTION-SCREEN BEGIN OF LINE.
SELECTION-SCREEN POSITION 1.
PARAMETERS: ch_exter AS CHECKBOX DEFAULT abap_true USER-COMMAND dest_external.
SELECTION-SCREEN COMMENT 3(8) text-006. " External Flights
SELECTION-SCREEN POSITION 12.
SELECTION-SCREEN COMMENT 13(4) v_exter. " Number of Internal Flights
SELECTION-SCREEN END OF LINE.

SELECTION-SCREEN END OF BLOCK block_1.


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

AT SELECTION-SCREEN.
*  o_airline_agent = zca_airline=>agent. (Wow! Not needed!)
  TRY.
      CALL METHOD o_airline_agent->get_persistent
        EXPORTING
          i_carrid = lb_airli
        RECEIVING
          result   = o_airline.

      v_currco = o_airline->get_currcode( ).
      v_url = o_airline->get_url( ).

    CATCH cx_os_object_not_found .

  ENDTRY.

START-OF-SELECTION.

  " Get Agent Instance (it uses a singleton)
  o_flight_schedule_agent = zca_flight_schedule=>agent.
  o_query_manager = cl_os_query_manager=>get_query_manager( ).

  TRY.
      CALL METHOD o_query_manager->if_os_query_manager~create_query
        EXPORTING
          i_filter     = `CARRID = PAR1`
*      i_ordering   =
*      i_parameters =
        RECEIVING
          result       = o_query
          .
    CATCH cx_os_class_not_found .
  ENDTRY.

  TRY.
      CALL METHOD o_flight_schedule_agent->if_os_ca_persistency~get_persistent_by_query
        EXPORTING
          i_query         = o_query
*    i_parameter_tab =
    i_par1          = lb_airli
*    i_par2          =
*    i_par3          =
*    i_subclasses    = OSCON_FALSE
*    i_upto          = 0
        RECEIVING
          result          = i_o_ref
          .
    CATCH cx_os_object_not_found .
    CATCH cx_os_query_error .
  ENDTRY.

  IF i_o_ref IS INITIAL.
    MESSAGE s005. " No flights shedules match selection
  ENDIF.

**********************************************************************
* output
**********************************************************************

    FORMAT COLOR 7.
    WRITE:
      AT 1 'Airline',
      AT 10 'I/E',
      AT 15 'Conn No',
      AT 25 'From',
      AT 30 'To'.

    NEW-LINE.
    FORMAT RESET.


  IF ch_inter = abap_true.
    WRITE: AT 1 lb_airli COLOR 1.
    WRITE: /.

    WRITE: AT 10 'I' COLOR 2.
    NEW-LINE.

    LOOP AT i_o_ref INTO o_ref.
      o_flight_schedule ?= o_ref.

      IF o_flight_schedule->get_countryfr( ) = o_flight_schedule->get_countryto( ).
        v_connid    = o_flight_schedule->get_connid( ).
        v_countryfr = o_flight_schedule->get_countryfr( ).
        v_countryto = o_flight_schedule->get_countryto( ).

        WRITE: AT 15 v_connid, AT 25 v_countryfr, AT 30 v_countryto.
        NEW-LINE.
      ENDIF.

    ENDLOOP.


    WRITE: AT 1(30) sy-uline, /.

  ENDIF.

  IF ch_exter = abap_true.
    WRITE: AT 1 lb_airli COLOR 1.
    WRITE: /.

    WRITE: AT 10 'E' COLOR 2.
    NEW-LINE.

    LOOP AT i_o_ref INTO o_ref.

      o_flight_schedule ?= o_ref.

      IF o_flight_schedule->get_countryfr( ) <> o_flight_schedule->get_countryto( ).
        v_connid    = o_flight_schedule->get_connid( ).
        v_countryfr = o_flight_schedule->get_countryfr( ).
        v_countryto = o_flight_schedule->get_countryto( ).

        WRITE: AT 15 v_connid, AT 25 v_countryfr, AT 30 v_countryto.
        NEW-LINE.
      ENDIF.

    ENDLOOP.

  ENDIF.

 

Vantagens

As vantagens do serviço de consulta são:

  • Encapsulamento da lógica de seleção de dados, facilitando o emprego do padrão MVC.
  • Possui integração com o serviço de persistência, permitindo que se trabalhe com os resultados de uma consulta já em formato de objetos

Desvantagens

  • O help deste serviço é bem pobre. Nada é falado sobre consultas que usam mais de uma tabela ou mais de uma classe persistente nem exemplos que utilizam muitos parâmetros.
  • Como a criação de consultas requerem strings, o código fica cheio delas e aparenta ficar sujo. Ainda, uso de constantes não parece gerar um bom ganho.
  • A criação de consultas se torna muito trabalhosa. Além do mais ainda não vi algum exemplo que funcione com expressões que utilizam expressões regulares (*) ou o operador IN. Cheguei até a abrir uma thread na SDN para isso.

Arquivos SAP Link

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: Renomear arquivo. Substituir a extensão .txt para .nugg

Próximo Episódio

No próximo post da série “Object Services”, veremos o serviço de transação, que encapsula os comandos COMMIT WORK e ROLLBACK WORK. Espero você lá! Mas antes…

Dê sua opinião!

Você já utilizou o serviço de consulta? Em qual ocasião? Teve bons resultados? O que achou de sua utilização?

Dê sua opinião também sobre este post! Use os botões de “curtir” ou de “retweet” caso tenha gostado!

Abraços a todos e boa semana!

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...