Banco de Dados - Caché
Como Resolver a Impedância em Banco de Dados
Este documento define diferença de impedância e fornece dois exemplos simples de como esta diferença afeta o desenvolvimento de aplicações.
por Equipe Linha de CódigoIntrodução
Com o amadurecimento e a grande aceitação do Java, a programação orientada a objeto assumiu o primeiro plano no cenário de desenvolvimento de aplicações. Devido aos seus ricos modelos de dados e ao suporte a conceitos que melhoram a produtividade, tais como encapsulamento, herança e polimorfismo, tecnologias de objetos como Java, C++ e COM são preferidas pelos atuais desenvolvedores de aplicações.
Entretanto, uma grande parte dos dados existentes no mundo ainda reside em bancos de dados relacionais. Os desenvolvedores de aplicações de bancos de dados (ou seja, qualquer aplicação que acesse dados armazenados) freqüentemente se vêem brigando com problemas de diferenças de impedância: a inerente falta de casamento entre os modelos de dados relacionais e os orientados a objeto. Os esforços para “mapear” dados relacionais em um formato utilizável de objetos freqüentemente prejudicam tanto a produtividade do programador quanto o desempenho da aplicação.
Todavia, a diferença de impedância pode ser aliviada por meio da escolha correta da tecnologia de banco de dados. Este documento define diferença de impedância e fornece dois exemplos simples de como esta diferença afeta o desenvolvimento de aplicações. A seguir, discute, em relação à diferença de impedância, os prós e contras de três tipos de bancos de dados: relacional, orientado a objeto e o Caché – o banco de dados multidimensional da InterSystems.
Entendendo a Diferença de Impedância
Diferença de impedância é uma expressão utilizada em engenharia elétrica, mas, na área de software, refere-se à inerente diferença que existe entre os modelos de dados relacionais e os orientados a objeto.
Para simplificar a questão, o modelo relacional organiza todos os dados em linhas e colunas. Cada linha representa um registro, e as colunas representam os diversos dados contidos em um registro. Se os dados forem por demais complexos para serem representados em uma grade bidimensional, tabelas adicionais são criadas para conter as informações “relacionadas”. Dessa forma, cada tabela em um esquema relacional conterá alguns, mas não todos os dados para uma grande quantidade de registros.
O modelo de dados orientado a objeto não está limitado a manter as informações em linhas e colunas. Em vez disso, o desenvolvedor cria uma definição – um modelo – que descreve completamente uma determinada classe de informação. Cada registro (objeto) é uma instância específica daquela classe. Assim, cada objeto contém todos os itens de informação para um, e apenas um, registro. Mas isso não é tudo. As definições de classe também podem incluirs trechos de programação, denominados métodos, que agem sobre os dados descritos pela classe. Não há uma concepção análoga no modelo relacional.
Um Exemplo Simples
Para ilustrar a diferença entre os dois modelos de dados, imagine que você está desenvolvendo um sistema de contas a receber. Sua aplicação sem dúvida terá de dar conta de várias notas fiscais, cada uma com alguma informação no cabeçalho (a data da nota fiscal, por exemplo), um número de nota fiscal e um ou mais itens de linha. Cada item de linha conterá, entre outras coisas, informações sobre o produto pedido e sua quantidade.
Uma forma de modelar a nota fiscal para um banco de dados relacional é criar duas tabelas. Uma delas (denominada Nota Fiscal) inclui as informações de cabeçalho que aparecem apenas uma vez em cada nota. Outra tabela (denominada ItensLinha) contém colunas para NotaFiscal_Mae, Cod_Prod_Item_Linha e Qtde_Item_Linha. A primeira coluna é especialmente importante, porque esse é o valor que “relaciona” os itens de linha às informações na tabela Nota Fiscal.
Observe que nenhuma tabela contém todas as informações sobre qualquer nota fiscal. Em vez disso, cada tabela contém algumas informações sobre muitas notas fiscais. Se você projetou sua aplicação, por exemplo, para imprimir uma nota fiscal, ela deve acessar as tabelas NotaFiscal e ItensLinha, para imprimir o cabeçalho e informações detalhadas, respectivamente. Observe, também, que as tabelas não contêm qualquer instrução sobre como formatar os dados para impressão. Tais instruções residem fora da base de dados propriamente dita.
No modelo orientado a objeto, os dados não precisam estar contidos em linhas e colunas. Assim, a definição da classe NotaFiscal se parecerá com uma lista de todos os itens de informação que constituem uma nota fiscal. Haverá propriedades contendo as informações de cabeçalho, tais como DataNF e NumeroNF, e uma coleção de uma ou mais instâncias da classe ItemLinha. Essa classe inclui as propriedades CodProd e QtdeItemLinha.
As definições de classe são meramente mapas do formato dos dados. Cada nota fiscal individual é uma instância específica da classe NotaFiscal e contém instâncias específicas da classe ItemLinha que lhe pertencem. Assim, cada objeto NotaFiscal contém todas as informações para uma dada nota fiscal, e apenas as informações referentes àquela nota.
Mas as definições de classe podem conter também métodos que agem sobre os dados descritos por aquela classe. Por exemplo, sua classe NotaFiscal poderá incluir um método Print( ) que informe como formatar para impressão os dados da nota fiscal. Objetos persistentes incluirão algum tipo de método Save( ) que especifique como os objetos serão armazenados na base de dados. A implementação default do método Save() será determinada pela estrutura do banco de dados, fornecida pelo fabricante deste. Diferenças de Impedância na Manipulação da Base de Dados
Considere o caso da criação, em seu sistema de contas a receber, de uma nova nota fiscal com um item de linha. Se você estivesse programando para uma base de dados relacional, seu código se pareceria com o apresentado no exemplo 1. Ele incluiria duas declarações Insert: uma para adicionar a informação de cabeçalho à tabela NotaFiscal e outra para incluir as informações detalhadas na tabela ItensLinha. Insert é um comando-padrão SQL e o fabricante do banco de dados relacional fornecerá sua implementação.
Listagem 1: Criando uma nova nota fiscal usando o modelo relacional
If (flag=”Novo”) { Insert Into NotaFiscal (Data_NF,Numero_NF) Values(Hoje,:NovoNumeroNF) Insert Into ItensLinha (NotaFiscal_Mae,Qtde_Item_Linha, Cod_Prod_Item_Linha) Values(:NovoNumeroNF,:Qtde,:ProdPedido) } O código para salvar uma nota fiscal com um item de linha, por meio do modelo orientado a objeto, é apresentado no exemplo 2. Exceto por detalhes sintáticos, ele é bastante similar ao exemplo relacional. A principal diferença consiste no fato de que o método Save( ) só é chamado uma vez. Exemplo 2: Criando uma nova nota fiscal usando o modelo orientado a objetos If (flag=”Novo”) { objNF=new NotaFiscal() objNF.DataNF=Hoje objNF.NumeroNF=NovoNumeroNF objLI=new ObjNF.ItemLinha() objLI.QtdeItemLinha=Qtde objLI.CodProd=ProdPedido objNF.Save() }
Agora imagine que você quer programar a lógica de negócios para sua aplicação em uma linguagem orientada a objeto, como Java ou C++, mas precisa armazenar seus dados em um banco de dados relacional. Para fazer isto em sua nota fiscal, as declarações SQL Insert devem ser programadas dentro do método Save( ) de sua definição de classe NotaFiscal. Eis uma manifestação de diferença de impedância: uma classe de objetos com uma coleção que deve ser traduzida para as tabelas díspares de um banco de dados relacional.
Diferenças de Impedância no Projeto
Outra forma de diferença de impedância pode aparecer durante o projeto da aplicação. Além de oferecer uma maneira mais rica e intuitiva de modelar os dados, a tecnologia de objetos engloba diversos conceitos que melhoram significativamente a produtividade do programador. Particularmente, a tecnologia de objetos suporta os conceitos de herança e polimorfismo.
A herança refere-se ao fato de uma definição de classe poder ser derivada de outra. Por exemplo, em seu sistema de contas a receber, você poderia criar a classe genérica ModeloNF e fazer com que as classes mais específicas NFSoftware e NFHardware herdassem propriedades e métodos dela. (Elas também podem incluir propriedades e métodos não-herdados, específicos de cada classe.) À medida que a aplicação evolui, se forem feitas modificações em ModeloNF, a herança faz com que essas mudanças se reflitam automaticamente nas definições de classe NFSoftware e NFHardware.
O polimorfismo diz respeito ao fato de diferentes implementações de um método poderem compartilhar uma interface. Por exemplo, o método Print( ) em NFSoftware e NFHardware pode incluir diferentes instruções de formatação etc. Entretanto, para imprimir uma nota fiscal, sua aplicação só precisa carregar um objeto na memória e chamar seu método Print( ). Graças ao polimorfismo, o objeto “saberá” como se formatar para impressão, de acordo com a classe a que pertence.
Nem herança nem polimorfismo existem no modelo relacional. Alguns grandes fabricantes de bancos de dados, tais como Oracle, Microsoft e IBM, tentaram implementar conceitos de projeto orientados a objeto, mas os resultados geralmente estão aquém das expectativas dos programadores.
Abordagens para Amenizar as Diferenças de Impedância
Os dois exemplos de diferença de impedância apresentados anteriormente são muito simplistas, mas servem para demonstrar o problema. O trabalho exigido para “normalizar” a diferença de impedância pode ser significativo, e ele aumenta drasticamente à medida que cresce a complexidade da aplicação. Entretanto, os efeitos da diferença de impedância podem ser substancialmente reduzidos por meio da escolha apropriada da tecnologia de banco de dados. Consideremos três opções de armazenamento de dados: um banco de dados relacional, outro “puramente” de objetos e o banco de dados multidimensional Caché.
Usando um Banco de Dados Relacional
Este documento já discutiu como a tentativa de usar um banco de dados relacional com uma aplicação baseada em tecnologia de objetos apresenta sérios problemas de diferença de impedância. Mas às vezes os desenvolvedores não têm escolha. Pode ser que eles tenham de acessar dados que residem em um banco de dados relacional. Nesse caso, uma opção é usar uma ferramenta de “mapeamento objeto-relacional”, quer seja ela autônoma, quer consista em facilidades disponíveis nos ditos bancos de dados “objeto-relacionais”.
Essencialmente, as ferramentas de mapeamento criam um arquivo (um mapa) que contém o código para a tradução entre objetos e tabelas relacionais. Os desenvolvedores devem especificar exatamente como a tradução será feita, ou seja, que propriedades do objeto correspondem a quais colunas em que tabelas, e vice-versa. Uma vez criado, o mapa é salvo e invocado sempre que uma aplicação move os dados de/e para o banco de dados. Algumas ferramentas de mapeamento objeto-relacional provêem um componente de cache em tempo de execução para ajudar a compensar a perda de desempenho causada pela tradução entre as formas relacional e de objetos.
Além de poder causar problemas de performance durante a execução, o mapeamento objeto-relacional pode atrasar significativamente o desenvolvimento da aplicação. A maioria das ferramentas de mapeamento não implementa conceitos de modelagem de objetos, como herança e polimorfismo, ou o faz apenas parcialmente. Assim, à medida que uma aplicação é adaptada e modificada, mapas objeto-relacionais novos e atualizados têm de ser criados.
Os desenvolvedores que enfrentam o problema de diferença de impedância entre aplicações orientadas a objeto e bancos de dados relacionais podem querer considerar a opção de migrar os dados para um sistema de armazenamento mais amigável. Eles devem, então, avaliar o esforço de reformatar e transferir seus dados uma só vez, em relação ao trabalho constante e às perdas de desempenho que resultam do uso de um mapa objeto-relacional.
Usando um Banco de Dados de Objetos
À primeira vista, pareceria que a diferença de impedância poderia ser totalmente eliminada armazenando-se os dados em um banco “puramente” de objetos. Isso é parcialmente verdade. Em geral, para uma aplicação orientada a objeto é fácil interagir com um banco de dados orientado a objeto. No entanto, neste cenário, a diferença de impedância ocorre quando se quer executar uma consulta SQL a essa base de dados. O SQL é, de longe, a linguagem de consulta mais amplamente utilizada em todo o mundo, e ele assume que os dados estão armazenados em tabelas relacionais. Alguns fabricantes de bancos de dados orientados a objeto fornecem o acesso a dados via linguagem de consulta de objeto (OQL, do inglês object query language), mas essas linguagens não têm aceitação generalizada. Para ser compatível com as aplicações comuns de análise de dados e de geração de relatórios, um banco de dados orientado a objeto deve suportar ODBC e JDBC e, portanto, prover algum mecanismo para representar os dados como tabelas relacionais.
A solução típica é, mais uma vez, o mapeamento. Os pontos negativos do mapeamento (perdas de performance e falta de suporte à evolução do modelo de dados) ainda se aplicam ao caso. O aspecto positivo é que o mapa só precisa ser chamado quando uma consulta SQL é feita à base de dados.
Usando o Caché Multidimensional com Arquitetura Unificada de Dados
Há uma terceira opção de armazenamento de dados: Caché, o banco de dados multidimensional da InterSystems. Apesar de se considerar que os bancos de dados multidimensionais estão no campo do data warehousing, o Caché foi projetado para ser uma parte das aplicações de processamento de transações. E ele implementa uma abordagem única para a redução de diferenças de impedância: a arquitetura unificada de dados.
Graças à arquitetura unificada de dados, os modelos de objetos e relacional “compartilham” os dados multidimensionais do Caché. Os vetores multidimensionais são facilmente representados como tabelas, porque elas nada mais são do que vetores bidimensionais. Da mesma forma, há uma correlação fácil entre vetores de objetos e multidimensionais, porque nenhum deles está limitado ao formato de linhas e colunas da tecnologia relacional. A tradução entre as formas de dados é automatizada e torna-se parte da definição de dados compilada. Para o desenvolvedor, cada tabela é efetivamente um objeto e cada objeto é uma ou mais tabelas.
Alguns Outros Atributos da Arquitetura Unificada de Dados do Caché
Concorrência total: As atualizações de dados feitas através da interface relacional são instantaneamente acessíveis por meio da interface de objetos e vice-versa.
Suporte à evolução do modelo de dados: As modificações na definição da estrutura de dados refletem-se automaticamente nas representações tanto de objetos quanto relacional.
Suporte total ao SQL: Todos os comandos SQL DDL, DML e DCL são suportados.
Suporte total a objetos:Todos os conceitos de modelagem de objetos, tais como heranças simples e múltipla, polimorfismo, tipos de dados avançados e geradores de métodos, são suportados.
Servidor de objetos:Os objetos definidos na arquitetura unificada de dados podem ser servidos como objetos Java, C++ ou COM, oferecendo compatibilidade com uma série de tecnologias orientadas a objeto.
A arquitetura unificada de dados pode reduzir drasticamente a diferença de impedância, mas não a eliminar completamente. Há alguns conceitos – por exemplo, métodos de objetos e disparadores (triggers) relacionais – que não podem ser automaticamente compartilhados. Entretanto, o Caché é uma boa opção para os desenvolvedores que estão buscando combinar o acesso a dados relacionais e a objetos.
Conclusões
Nos últimos anos, as linguagens de programação orientadas a objeto, tais com Java, C++ e COM, tornaram-se as tecnologias dominantes para o desenvolvimento de aplicações. Assim, os desenvolvedores de aplicações de bancos de dados precisam ser capazes de representar dados como objetos. No entanto, o SQL é, de longe, a tecnologia dominante para análise de dados e geração de relatórios. Desta forma, para ser útil, qualquer banco de dados deve ser capaz de representar dados como tabelas relacionais que podem ser acessadas via ODBC e JDBC.
A diferença de impedância – a inerente falta de casamento entre os modelos de dados relacionais e os orientados a objeto – não pode ser evitada, mas pode ser significativamente atenuada por meio da escolha correta da tecnologia de banco de dados. Para o desenvolvimento de novas aplicações, faz sentido armazenar os dados em um banco de dados multidimensional como o Caché, que, através de sua arquitetura unificada de dados, permite que os dados sejam representados tanto como objetos quanto como tabelas.
Para a manutenção de aplicações, nas quais os dados existentes já estão armazenados, digamos, em um banco de dados relacional, os desenvolvedores deveriam considerar a conversão desses dados para a forma multidimensional. Por meio de um esforço único para converter seus dados, eles evitarão os prejuízos de performance e as dores de cabeça na evolução do modelo de dados, que são endêmicos ao mapeamento objeto-relacional.
Mary A. Finn Gerente de Produto