Mapeando Objetos para Bancos de Dados Relacionais

Propaganda
Artigo: Mapeando Objetos para Bancos de Dados Relacionais: técnicas e implementações.
Autor: Herval Freire de A. Júnior – é programador Java, web components developer
certificado pela Sun e graduado em Tecnologia em Telemática pelo CEFET-PB.
Publicado: http://www.mundooo.com.br/php/mooartigos.php?pa=showpage&pid=19
Data Publicação: 23/02/2003
Mapeando Objetos para Bancos de Dados Relacionais: técnicas e
implementações
Introdução
O surgimento do desenvolvimento "Orientado a Objetos" ocasionou uma mudança
radical na estruturação e organização da informação. Entretanto, os bancos de dados
utilizados continuaram sendo relacionais. Devido a isto, é comum a adaptação dos modelos
de objetos na tentativa de compatibilizá-los com o modelo relacional. Além disso, é notório
o esforço aplicado no processo de persistência manual dos objetos no banco de dados –
onde os desenvolvedores precisam dominar a linguagem SQL e utilizá-la para realizar
acessos ao banco de dados. Como conseqüência ocorre uma redução considerável na
qualidade do produto final, construção de uma modelagem "orientada a objetos"
inconsistente e a um desperdício considerável de tempo na implementação manual da
persistência.
Para permitir um processo de mapeamento entre sistemas baseados em objetos e
bases de dados relacionais, foram propostas diversas idéias que encaminhar-se para o
conceito de Camada de Persistência.
Evolução dos modelos de Persistência de Objetos
Na grande maioria dos sistemas, incluir código SQL para acesso ao SGBD em meio
ao restante da lógica do sistema é a solução adotada, graças à rapidez de implementação.
Esta solução é perigosa, pois implica, muitas vezes, no acoplamento do sistema ao SGBD
utilizado, o que dificulta a manutenção do código. Além disso, qualquer mudança na
estrutura das tabelas existentes no Banco de Dados trazem o caos à aplicação.
Com intuito de diminuir este acoplamento, surge a opção de separar o código SQL
das classes da aplicação, de forma que as alterações no modelo de dados requerem
modificações apenas nas classes de acesso a dados (Data Access Classes), restringindo o
impacto das mudanças no sistema como um todo. No entanto, apesar da limpeza de código
e melhor divisão de responsabilidades, a solução está longe de ser ideal, por ainda manter
os dois mundos (objetos e dados relacionais) intimamente ligados.
A principal função de uma camada de abstração de acesso a dados é garantir aos
desenvolvedores de software a total independência entre o modelo de objetos e o esquema
de dados do banco, permitindo que a base ou detalhes do esquema de dados sejam
substituídos sem impacto nenhum na aplicação.
Camadas de Persistência
Conceitualmente, uma Camada de Persistência de Objetos é uma biblioteca que
permite a realização do processo de persistência (isto é, o armazenamento e manutenção do
estado de objetos em algum meio não-volátil, como um banco de dados) de forma
transparente. A utilização deste conceito permite ao desenvolvedor trabalhar como se
estivesse em um sistema completamente orientado a objetos, utilizando métodos para
incluir, alterar e remover objetos e uma linguagem de consulta para realizar consultas que
retornam coleções de objetos instanciados.
Vantagens da utilização
As vantagens do uso de uma Camada de Persistência são: isolar os acessos
realizados diretamente ao banco de dados na aplicação, bem como centraliza os processos
de construção de consultas (queries) e operações de manipulação de dados (insert, update
e delete) em uma camada de objetos inacessível ao programador.
Requisitos de uma Camada de Persistência
Segundo Scott Ambler, uma Camada de Persistência
características:
 Dar suporte a diversos tipos de

mecanismos de persistência

 Encapsulamento
completo da

camada de dados

 Ações com multi-objetos
 Transações

 Extensibilidade

 Identificadores de Objetos

real deve implementar as seguintes
Cursores e Proxies
Registros
Arquiteturas Múltiplas
Diversas versões de banco de
dados e fabricantes
Múltiplas conexões
Queries SQL
Controle de Concorrência
Object IDs
Os Object Ids (OIDs) são identificadores únicos de elementos do esquema de dados.
Eles funcionam como um tipo de chave primária nos sistemas Orientados a Objetos,
mantendo relacionamentos entre objetos e garantindo a unicidade dos objetos em todo o
esquema.
Mapeando objetos para Tabelas
Existem diversas técnicas que permitem o mapeamento de conjuntos de objetos,
cada qual com suas vantagens e desvantagens sobre as demais. Em geral, uma camada de
persistência implementa uma destas técnicas e o desenvolvedor que for utilizá-la deve
organizar as tabelas em seu banco de dados de acordo com o esquema de objetos.
Mapeando atributos
Ao transpor-se um objeto para uma tabela relacional, os atributos do mesmo são
mapeados em colunas da tabela. Este processo de mapeamento deve levar em consideração
fatores como a tipagem e o comprimento máximo dos campos. Também é importante
ressaltar que, nem sempre atributos de um objeto são obrigatoriamente uma coluna em uma
tabela. Além disso, existem casos onde um atributo pode ser mapeado para diversas colunas
ou vários atributos podem ser mapeados para uma mesma coluna.
Mapeamento de classes em tabelas
As três técnicas de mapeamento de objetos mais comumente implementadas são:
 Mapeamento de uma tabela por hierarquia: Toda a hierarquia de classes deve ser
representada por uma mesma tabela: uma coluna que identifique o tipo do objeto
(Object Type) serve para identificar a classe do objeto representado por cada linha
na tabela, quando nenhum outro modo de identificação é viável. As desvantagens
são: a ausência de normalização e para hierarquias de classes com muitas
especializações existe uma proliferação de campos com valores nulos.
 Mapeamento de uma tabela por classe concreta: Nesta estratégia, teremos uma
tabela para cada classe concreta presente em nosso sistema. A tabela identifica a
classe de todos os elementos contidos na mesma, tornando desnecessário o
mecanismo de Object Type. Essa estratégia leva à redundância de dados: quaisquer
atributos definidos em uma classe abstrata na hierarquia devem ser criados em
todas as tabelas que representam classes-filhas da mesma. Além disso, mudar o tipo
(especializar ou generalizar) de um objeto torna-se um problema, já que é
necessário transferir todos os seus dados de uma tabela para outra no ato
da atualização.
 Mapeamento de uma tabela por classe: Criamos uma tabela para cada classe da
hierarquia, relacionadas através do mecanismo de especialização padrão do banco
de dados (utilização de chaves estrangeiras). Segundo esta estratégia, tenta-se ao
máximo manter a normalização de dados, de forma que a estrutura final das tabelas
fica bastante parecida com a hierarquia das classes representada pela UML. A
colocação de um identificador de tipo (Object Type) na classe-pai da hierarquia
permite identificar o tipo de um objeto armazenado nas tabelas do sistema sem
forçar junções entre as tabelas, garantindo melhorias na performance, e é uma
estratégia comumente utilizada. A quantidade de junções (joins) entre tabelas para
obter todos os dados de um objeto o seu principal ponto negativo.
A tabela a seguir, faz um comparativo destas três técnicas de mapeamento de classes.
Ad-hoc reporting
Facilidade de implementação
Facilidade de acesso
Acoplamento
Velocidade de acesso
Suporte a polimorfismos
Uma tabela por
hierarquia de classes
Simples
Simples
Simples
Muito alto
Rápido
Médio
Uma tabela por
classe concreta
Médio
Médio
Simples
Alto
Rápido
Baixo
Uma tabela por
classe
Médio/Difícil
Difícil
Médio/Simples
Baixo
Médio/Rápido
Alto
Mapeamento de Relacionamentos
Relacionamentos um-para-um necessitam que uma chave estrangeira seja colocada
em uma das duas tabelas, relacionando o elemento associado na outra tabela. Para manter
relacionamentos um-para-muitos, adota-se a mesma técnica: uma referência na forma
de chave estrangeira deve ser posta na tabela que contém os objetos múltiplos. No caso de
relacionamentos muitos-para-muitos, convenciona-se criar uma tabela intermediária que
armazene pares de chaves, identificando os dois lados do relacionamento.
Camadas de Persistência e Linguagens de Programação
Existem diversas implementações (bibliotecas) de camadas de persistência. Estas
bibliotecas muitas vezes tratam da geração dos esquemas de dados automaticamente e
podem realizar engenharia reversa do banco de dados, criando a hierarquia de classes a
partir do esquema de tabelas do banco de dados.
Abaixo, segue a lista com algumas das implementações mais utilizadas.
 Hibernate – permite a persistência transparente de objetos em bases de
dados utilizando JDBC e o mapeamento de classes para XML. Trata-se de um
serviço de persistência e recuperação de objetos, já que, ao contrário dos
frameworks de persistência, não é necessário estender nenhuma classe especial
para que um objeto possa ser armazenado. Projetado para permitir integração com
ambientes J2EE, o Hibernate utiliza reflexão para tratar a persistência, gerando
código SQL à medida que for necessário.
 Castor – um framework de ligação de dados (databinding), que propõe-se a ser "a
menor distância entre objetos Java, documentos XML, diretórios LDAP e dados
SQL", promovendo mapeamentos entre todas estas estruturas de representação de
objetos.
 Object-Relational Java Bridge (OJB) - projeto do grupo Apache para prover uma
implementação open-source dos padrões de mapeamento de objetos ODMG e JDO.
OJB permite que objetos sejam manipulados sem a necessidade de implementar
nenhuma interface em especial ou estender alguma classe específica. A
biblioteca dá suporte a cenários cliente-servidor (aplicações distribuídas) ou
standalone, de forma que é possível utilizar a API OJB para persistência de objetos
mesmo em ambientes J2EE.
Conclusão
Apesar da relutância em adotar esquemas de persistência, fica evidente que sua
utilização trás um ganho considerável reduzindo o tempo de implementação e aumentando
a qualidade do produto final, à medida que diminui a possibilidade de erros de codificação.
Além de fornecer um acesso mais natural aos dados, as Camadas de Persistência podem
ainda executar controle transacional, otimização de consultas e transformação automática
de dados entre formatos distintos (tabelas relacionais para arquivos XML ou classes
Java, por exemplo).
Download