1
Anderson Gomes da Silva
0205109 8º Semestre
Mapeamento Objeto-Relacional
Monografia apresentada à disciplina Trabalho
de Conclusão do Curso de Ciências da
Computação da Faculdade de Jaguariúna, sob.
a orientação do Prof. Leonardo Hartleben Reinehr,
como exigência parcial para conclusão do
curso de graduação.
Jaguariúna
2005
2
Silva, Anderson Gomes. Estudo Comparativo de Ferramentas de Mapeamento ObjetoRelacional, Monografia defendida e aprovada na FAJ em 15 de Dezembro de 2005 pela
banca examinadora constituída pelos professores:
____________________________________________________
Profº. Leonardo Hartleben Reinehr –
FAJ - Orientador
____________________________________________________
Profº. Odersom
____________________________________________________
Profº. Roberto Pacheco
3
Aos meus pais Vital e Maria do Carmo
e irmãos Helinton e Erica.
4
Agradecimentos
Primeiramente à Deus pois sem ele nada é possível;
Aos meus pais Vital e Maria do Carmo, por toda educação e amor que
foram investidos em mim durante a minha vida;
Aos meus irmão e minha namorada que me incentivaram neste trabalho;
Aos meus professores, pelas conversas, conselhos e ensinamento que
servirão para a vida toda;
Ao meu orientador Leonardo Hartleben Reinehr, pela confiança e apoio
depositados em mim;
À todos meus amigos: Leonardo, Michel, Robson, Juliano, pela amizade e
apoio, algo que vai durar muito mais do que quatro anos;
À todas as pessoas que me ajudaram nesta etapa da minha vida;
5
"É muito melhor arriscar coisas grandiosas,
alcançar triunfos e glórias, mesmo expondo-se a
derrota, do que formar fila com os pobres de
espírito que nem gozam muito nem sofrem muito,
porque vivem nessa penumbra cinzenta que não
conhece vitória nem derrota."
(Theodore Roosevelt)
6
SILVA, Anderson Gomes. Estudo Comparativo de Ferramentas de Mapeamento
Objeto-Relacional. 2005. Monografia. (Bacharelado em Ciências da Computação) –
Curso de Ciências da Computação da Faculdade de Jaguariúna, Jaguariúna.
RESUMO
Este trabalho é um estudo comparativo entre as Ferramentas de Mapeamento
Objeto-relacional, com enfoque ao Banco de Dados Objeto-Relacional. E tem como
objetivo avaliar o estado da arte da teoria de mapeamento objeto-relacional, identificando
as características e necessidades desse mecanismo, ele também ira mostrar os principais
frameworks para mapeamento objeto-relacional, identificando as vantagens de sua
utilização, funcionalidades oferecidas e características de implementação de acordo com a
teoria de mapeamento objeto-relacional.
Mostrara também a implementação de um estudo de caso utilizando os frameworks
estudados, comparando os resultados obtidos em termos de funcionalidades, performance,
flexibilidade e facilidade de uso entre outros aspectos.
Palavras-chave: Banco de Dados Relacional, Ferramentas de Mapeamento ObjetoRelacional.
ABSTRACT
This work is a comparative study between the Relational-object Mapping Tools,
with approach to the Database Relational-object. Its target is to evaluate the art state of
the theory of mapping relational-object, and identify the characteristics and needs of this
mechanism, it will also show the main frameworks to mapping relational-object, to
identify the advantages of its use, offered functionalities and implementation
characteristics according to the theory of mapping relational-object.
It will also show the implementation of a case-study using the analyzed frameworks,
comparing the acquired results in functionalities terms, performance, flexibility and
facilities, and others aspects.
Key-Word: Relationary data base, Tools of Objeto-Relacional Mapping.
7
LISTA DE ABREVIATURAS E SIGLAS
API
CASE
OID
OO
OO-ER
UML
XMI
XML
OQL
SGBD
SGBDOO
SGBDOR
SGBDR
SQL
ER
Application Programming Interface
Computer Aided Software Engineering
Object Identification
Orientado a Objeto
Orientado a Objeto Entidade Relacional
Unified Modeling Language
XML Metadata Interchange
eXtensible Markup Language
Object Query Language
Sistema Gerenciador de Banco de Dados
Sistema Gerenciador de Banco de Dados Orientado a Objeto
Sistema Gerenciador de Banco de Dados Objeto Relacional
Sistema Gerenciador de Banco de Dados Relacionais
Structured Query Language
Entidade Relacionamento
8
LISTA DE FIGURAS
FIGURA 1
FIGURA 2
FIGURA 3
FIGURA 4
FIGURA 5
FIGURA 6
FIGURA 7
FIGURA 8
FIGURA 9
Mapeamento Básico
Mapeamento de Relacionamento (um-para-um)
Mapeamento de Relacionamento (um-para-muitos)
Mapeamento de Relacionamento (muitos-para-muitos)
Uma Tabela para toda Hierarquia
Uma Tabela por Classe Concreta
Uma Tabela por Classe
Uma Visão Simplista do Hibernate
Componentes do Hiberante
9
LISTAS DE TABELAS
TABELA 1
TABELA 2
TABELA 3
TABELA 4
TABELA 5
TABELA 6
TABELA 7
TABELA 8
TABELA 9
TABELA 10
Comparativo entre técnicas de mapeamento de classe
Parâmetros principais para configuração da conexão JDBC
Parâmetros que definem comportamentos em tempo de execução
Criando um SessionFactory através do objeto configuration
Classe candidata a persistência
Implementação da classe pessoa
Declaração de criação da tabela que armazena a classe pessoa
Mapeando a classe pessoa numa tabela
Tipos de geradores presentes no Hiberante
Mapeamento da classe pessoa com parâmetros opcionais
TABELA 11
Associação entre os tipos do Hibernate, classes Wrapper Java e tipo no BD
TABELA 12
TABELA 13
TABELA 14
TABELA 15
Mapeamento conteplado no SessionFactory
Banco de dados suportados pelo Hibernate
Comparações do Hibernate com outros frameworks de persistências
Diagrama de Classe “Biblioteca Música”
10
SUMÁRIO
LISTA DE ABREVIATURA E SIGLAS......................................................................7
LISTAS DE FIGURAS..................................................................................................8
LISTAS DE TABELAS.................................................................................................9
1. INTRODUÇÃO………………...............................................................................11
2. REVISÃO BIBLIOGRÁFICA……………………................................................21
3. MAPEAMENTO OBJETO RELACIONAL……………...................................... 39
4. FERRAMENTAS DE MAPEAMENTO OBJETO RELACIONAL………..........59
5. ESTUDO DE CASO…………………...................................................................66
6. RESULTADOS OBTIDOS.....................................................................................68
7. CONCLUSÕES.......................................................................................................70
8. REFERÊNCIA BIBLIOGRÁFICA.........................................................................71
11
1. INTRODUÇAO
Desde seu desenvolvimento até os dias atuais, bancos de dados relacionais sempre
foram os mais utilizados no cenário comercial [DATE, SILBERSCHATZ]. Por outro lado,
nos últimos anos houve uma crescente disseminação das linguagens orientadas a objeto no
desenvolvimento de aplicações. Dessa forma, hoje existe um grande número de aplicações
orientadas a objeto que acessam bancos de dados relacionais.
Existe uma notada incompatibilidade entre o modelo orientado a objetos e o modelo
relacional [AGILE DATA], a qual dificulta a transformação dos dados armazenados em um
modelo para o outro. Por isso, é importante a existência de mecanismos para realizar o
mapeamento das classes do modelo orientado a objeto para tabelas do modelo relacional.
A teoria de mapeamento objeto-relacional define um conjunto de características
necessárias a tais mecanismos, além de apresentar possíveis soluções para os problemas de
incompatibilidade entre os modelos [DATE, SILBERSCHATZ, AGILE DATA].
Os frameworks de mapeamento objeto-relacional oferecem enormes facilidades para
a realização desse tipo de mapeamento, implementando as soluções indicadas na teoria de
mapeamentos e permitindo que as aplicações executem mapeamentos por meio de
configuração e definição de regras ao invés da escrita de linhas de código [Hibernate].
O presente trabalho tem por objetivo mostrar as principais características de um
Mapeamento Objeto-Relacional, com enfoque em um estudo comparativo de ferramentas
de mapeamento e na identificação de problemas e questões em aberto das ferramentas
existentes. Para isso, será implementado um estudo de caso usando diversas tecnologias e
frameworks existentes, comparando os resultados obtidos.
12
2. REVISÃO BIBLIOGRÁFICA
2.1. BANCO DE DADOS RELACIONAIS
Um banco de dados é um conjunto de informações com uma estrutura regular. Um
banco de dados é normalmente, mas não necessariamente, armazenado em algum formato
de máquina lido pelo computador. Há uma grande variedade de banco de dados, desde
simples tabelas armazenadas em um único arquivo até gigantescos bancos de dados com
muitos milhões de registros, armazenados em salas cheias de dados rígidos. Os banco de
dados caracteristicamente moderno são desenvolvidos desde os anos da década de 1960,
um dos pioneiros neste trabalho foi Charles Bachman. Existe uma grande variedade de
banco de dados, desde exemplos simples como uma simples coleção de tabelas até um
modelo teoricamente definido, o relacional [AGILE DATA].
O modelo de dados lógico relacional é atualmente o mais utilizado nos SGBDs
comerciais. Entretanto, este modelo possui um sistema de tipos simples e restrito, o que
dificulta a descrição de algumas aplicações atuais que necessitam tipos mais complexos e
características do modelo Orientado a Objetos.
Sistemas de Banco de Dados que utilizam o modelo relacional, ou seja, SGBDRs,
são também considerados sistemas de segunda geração de SGBDs visto que os sistemas de
Banco de Dados Hierárquicos e de Rede são considerados a primeira geração. Assim, os
sistemas de Banco de Dados Objeto Relacionais são classificados como a terceira geração
de SGBDs.
2.2. ORIENTAÇÃO A OBJETO
Orientação a objeto pode ser definida como um conjunto de disciplinas de
modelagem de software que facilitam a construção sistemas complexos a partir de
componentes individuais). O apelo intuitivo da orientação a objeto é que ele proporciona
conceitos e ferramentas para modelar e representar o mundo real. As vantagens da
orientação a objeto na programação e na modelagem de dados são muitas[AGILE DATA].
13
A programação de objeto (orientada) permite uma representação mais direta do
modelo do mundo real no código. O resultado é que a transformação
radical
das
requisições do sistema (definido em termos de usuários) para especificação do sistema
(definido em termos de computador) é enormemente reduzida.
2.3. IMPEDÂNCIA
Os desenvolvedores de aplicações de bancos de dados (ou seja, qualquer aplicação
que acesse dados armazenados em um banco de dados) 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 frequentemente prejudicam tanto a
produtividade do programador quanto o desempenho da aplicação.
A diferença de impedância é uma expressão utilizada em engenharia elétrica, mas
no contexto deste trabalho, refere-se à diferença que existe entre os modelos de dados
relacional e objeto. O modelo relacional organiza todos os dados em linhas e colunas, onde
cada linha representa um registro. Se os dados forem por demais complexos para serem
representados em forma de tabela, tabelas adicionais são cridas para conter as informações
“relacionadas”. Dessa forma, cada tabela em um esquema relacional conterá registro 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ções. Cada registro (objeto) é
uma instância específica daquela classe. Assim, cada registro contém todos os itens de
informação para um, e apenas um, registro. Mas isso não é tudo, as definições de classes
também podem incluir trechos de programação, denominados métodos que apenas sobre os
dados descritos pela classe. Não há uma concepção análoga no modelo relacional.
2.3.1. Usando um banco de dados relacional
14
Esta seção já discute 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 as vezes os desenvolvedores não tem escolha. Pode ser que eles tenham
de acessar dados que residem em um banco de dados relacional. Nesse caso, uma opção é
usar ema ferramenta de “mapeamento objeto-relacional”, quer seja ela autônoma, quer
consiste em facilidades disponível nos bancos de dados “objeto-relacional”.
Essencialmente, as ferramentas de mapeamento criam um arquivo (um mapa) que contém
as regras 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 de que tabelas e vice-versa. Uma vez criado, o mapa é salvo
e invocado sempre que uma aplicação move os dados para o banco de dados. Algumas
ferramentas de mapeamento objeto relacional provêem um componente de cachê em tempo
de execução para ajudar a compensar a perda de desempenho causada pela tradução entre
as formas relacionais e de objetos.
Além de poder causar problema 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-relacional novos e atualizados têm de
ser criados.
Os desenvolvedores que enfrentam o problema da diferença de impedância entre
aplicação orientadas a objeto e bancos de dados relacionais relacional 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 as perdas de desempenho que resultam do uso de um mapa
objeto-relacional.
2.3.2. Usando um banco de dados de objeto
À primeira vista, parecia que a diferença de impedância poderia ser totalmente
eliminada armazenando-se os dados em um banco “puramente” de objetos. Isso é
15
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. A SQL é, de longe
a linguagem de consulta mais amplamente utilizada em todo o mundo, e ela assume que os
dados estão armazenados em tabelas do modelo relacional. Alguns fabricantes de banco 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 analise de dados e de geração de
relatórios, um banco de dados orientado a objeto deve 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 de dados) ainda se aplicam ao caso. O aspecto positivo é que o
mapeamento só precisa ser chamado quando uma consulta SQL é feita á base de dados.
2.4. CAMADA DE PERSISTÊNCIA
Podemos definir persistência de dados como uma forma de manter a existência da
informação mesmo fora da aplicação. Podemos persistir a informação em um banco de
dados, em um arquivo de dados ou qualquer outro meio existente e o fato da informação
existir também fora da aplicação faz com que essas informações possam ser compartilhadas
por outras aplicações.
Para permitir um processo de mapeamento entre sistemas baseados em objetos e bases
de dados relacionais, foram propostas diversas idéias que convergiram para o conceito de
Camada 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. Graças à independência entre a camada de persistência e o repositório de
dados utilizado, também é possível gerenciar a persistência de um modelo de objetos em
diversos tipos de repositórios, com pouco ou nenhum esforço extra. A utilização deste
16
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 SGBDs Orientados a Objetos – comumente a
linguagem OQL – para realizar consultas que retornam coleções de objetos instanciados.
2.4.1. Vantagens da utilização
As vantagens decorrentes do uso de uma Camada de Persistência no
desenvolvimento de aplicações são evidentes: a sua utilização isola os acessos realizados
diretamente ao banco de dados na aplicação, bem como centraliza os processos de
construção de consultas e operações de manipulação de dados (insert, update e delete) em
uma camada de objetos inacessível ao programador. Este encapsula mento de
responsabilidades garante maior confiabilidade às aplicações e permite que, em alguns
casos, o próprio SGBD ou a estrutura de suas tabelas possam ser modificados, sem trazer
impacto à aplicação nem forçar a revisão e recompilação de códigos.
2.4.2. Requisitos de uma camada de persistência
Segundo Scott Ambler, pesquisador e autor de diversos livros, uma Camada de
Persistência real deve implementar as seguintes características:
•
Dar suporte a diversos tipos de mecanismos de persistência: um mecanismo de
persistência pode ser definido como a estrutura que armazenará os dados – seja ela
um SGBD relacional, um arquivo XML ou um SGBD OO, por exemplo. Uma
Camada de Persistência deve suportar a substituição deste mecanismo livremente e
permitir a gravação de estado de objetos em qualquer um destes meios.
•
Encapsula mento completo da camada de dados: o usuário do sistema de
persistência de dados deve utilizar-se, no máximo, de mensagens de alto nível como
save ou delete para lidar com a persistência dos objetos, deixando o tratamento
destas mensagens para a camada de persistência em si.
•
Ações com multi-objetos: Suportar listas de objetos sendo instanciadas e retornadas
da base de dados deve ser um item comum para qualquer implementação, tendo em
vista a freqüência desta situação.
17
•
Transações: ao utilizar-se da Camada de Persistência, o programador deve ser capaz
de controlar o fluxo da transação – ou ter garantias sobre o mesmo, caso a própria
Camada de Persistência preste este controle.
•
Extensibilidade: A Camada de Persistência deve permitir a adição de novas classes
ao esquema e a modificação fácil do mecanismo de persistência.
•
Identificadores de Objetos: A implementação de algoritmos de geração de chaves de
identificação garante que a aplicação trabalhará com objetos com identidade única e
sincronizada entre o banco de dados e a aplicação.
•
Cursores e Proxies: As implementações de serviços de persistência devem ter
ciência de que, em muitos casos, os objetos armazenados são muito grandes – e
recuperá-los por completo a cada consulta não é uma boa idéia. Técnicas como o
lazy loading (carregamento tardio) utilizam-se dos proxies para garantir
que atributos só serão carregados à medida que forem importantes para o cliente e
do conceito de cursores para manter registro da posição dos objetos no banco de
dados (e em suas tabelas específicas).
•
Registros: Apesar da idéia de trabalhar-se apenas com objetos, as camadas de
persistência devem, no geral, dispor de um mecanismo de recuperação de registros conjuntos de colunas não encapsuladas na forma de objetos, como resultado de suas
consultas. Isto permite integrar as camadas de persistências a mecanismos de
geração de relatórios que não trabalham com objetos, por exemplo, além de permitir
a recuperação de atributos de diversos objetos relacionados com uma só consulta.
•
Arquiteturas Múltiplas: O suporte a ambientes de programas stand-alone, cenários
onde o banco de dados encontra-se em um servidor central e mesmo arquiteturas
mais complexas (em várias camadas) deve ser inerente à Camada de Persistência, já
que a mesma deve visar a reusabilidade e fácil adaptação a arquiteturas distintas.
•
Diversas versões de banco de dados e fabricantes: a Camada de Persistência deve
tratar de reconhecer diferenças de recursos, sintaxe e outras minúcias existentes no
acesso aos bancos de dados suportados, isolando isto do usuário do mecanismo e
garantindo portabilidade entre plataformas.
18
•
Múltiplas conexões: Um gerenciamento de conexões (usualmente utilizando-se de
pooling) é uma técnica que garante que vários usuários utilizarão o sistema
simultaneamente sem quedas de performance.
•
Queries SQL: Apesar do poder trazido pela abstração em objetos, este mecanismo
não é funcional em cem porcento dos casos. Para os casos extremos, a Camada de
Persistência deve prover um mecanismo de queries que permita o acesso direto aos
dados – ou então algum tipo de linguagem de consulta similar à SQL, de forma a
permitir consultas com um grau de complexidade maior que o comum.
•
Controle de Concorrência: Acesso concorrente a dados pode levar a inconsistências.
Para prever e evitar problemas decorrentes do acesso simultâneo, a Camada de
Persistência deve prover algum tipo de mecanismo de controle de acesso. Este
controle geralmente é feito utilizando-se dois níveis – com o travamento
pessimístico (pessimistic locking), as linhas no banco de dados relativas ao objeto
acessado por um usuário são travadas e torna-se inacessíveis a outros usuários até o
mesmo liberar o objeto. No mecanismo otimístico (optimistic locking), toda a
edição é feita em memória, permitindo que outros usuários venham a modificar o
objeto.
2.4.3. Camadas de persistência e linguagens de programação
Diversas
implementações
de camadas
de
persistência estão
disponíveis
gratuitamente na Internet. Estas bibliotecas muitas vezes tratam da geração dos esquemas
de dados (mapeamentos) automaticamente e podem até mesmo efetuar uma engenharia
reversa – criando hierarquia de classes a partir de um esquema de tabelas em banco de
dados. As Camadas de Persistência que geralmente trabalham com apenas um esquema de
mapeamento de classes para tabelas, diversas estratégias de geração de identificadores,
suporte a quaisquer tipos de relacionamento e geração de código SQL automatizada.
Na linguagem Java, podemos citar algumas destas bibliotecas de persistência:
•
Hibernate – uma implementação que 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
19
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 (reflection) para tratar a persistência,
gerando código SQL à medida que for necessário. Atualmente compatível com 11
SGBDs comerciais em sua versão 1.1 (Oracle, DB2, MySQL, PostgreSQL, Sybase,
SAP DB, HypersonicSQL, Microsoft SQL Server, Progress, Mckoi SQL, Pointbase
e Interbase), o Hibernate é distribuído segundo a licença LGPL e suporta uma API
baseada no padrão ODMG 3.0 (o padrão para construção de SGBDs Orientados a
Objetos). Dentre outros recursos interessantes, o Hibernate suporta gerenciamento
remoto utilizando-se a API JMX e é capaz de gerar esquemas de dados (tabelas)
para representar hierarquias de classes.
•
Castor – um framework de ligação de dados (databinding), o Castor 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. A API do pacote Castor específica para a persistência em bancos de
dados relacionais é a JDO – uma implementação inspirada no padrão Java Data
Objects da Sun. A API provê integração com ambientes J2EE. Atualmente em sua
versão 0.9, o Castor suporta os SGBDs Oracle, Sybase, SQL Server, DB2, Informix,
PostgreSQL, Hypersonic SQL, InstantDB, Interbase, MySQL e SAP DB. A
distribuição segue a licença LGPL.
•
Object-Relational Java Bridge (OJB) - um projeto do grupo Apache para prover
uma implementação open-source dos padrões de mapeamento de objetos ODMG e
JDO, o 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. Além disso, a biblioteca possui integração com o sistema de geração de
logs. Em sua versão 0.9, o OJB dá suporte a configuração de esquemas em tempo de
execução, geração de tabelas para mapear uma hierarquia de classes ou classes
relativas a um conjunto de tabelas e implementa uma série de elementos que visam
melhorar a performance da Camada de Persistência. Os SGBDs suportados pela
20
implementação atual incluem DB2, Hypersonic SQL, Informix, MS-Access, MSSQL Server, MySQL, Oracle, PostgreSQL, Sybase e SAP DB. A distribuição é
feita segundo a licença Apache.
•
Torque – um framework de persistência desenvolvido como subprojeto do projeto
Apache Turbine, a API trabalha gerando toda a estrutura de banco de dados, classes
e código SQL para acesso aos dados relativos a um esquema pré-configurado. O
esquema é escrito na forma de um arquivo XML, que é interpretado pela
biblioteca utilizando o Ant, uma ferramenta de compilação de código (build tool) do
projeto Apache. A API torque encontra-se em sua versão 3.0 e é distribuída segundo
a licença Apache.
2.5. O QUE SÃO FRAMEWORKS
Um framework OO é uma estrutura de classes inter-relacionadas que constitui uma
implementação inacabada, para um conjunto de aplicações de um domínio, além de ser uma
técnica que faz o reuso do projeto.
O termo framework que inicialmente estava associado ao conceito de bibliotecas de
classes reutilizáveis, mais recentemente, teve seu conceito estendido para qualquer solução
incompleta que pode ser completada através da instanciação, possibilitando a criação de
mais uma aplicação dentro do domínio-alvo do framework (Esta definição tem
similaridades com a do gerador de artefatos).
Atualmente esta técnica tem sido muito apoiada e utilizada pela comunidade de
desenvolvimento de SI. Há uma vasta gama de frameworks disponíveis, tanto na forma de
software livre quanto proprietário, alguns mais restritos, outros mais flexíveis.
É necessário conhecer bem um framework antes de adotá-lo em seu projeto, muitos
ainda são muito imaturos e podem condenar o software a um curto período de sucesso.
21
3. MAPEAMNTO OBJETO RELACIONAL
O termo Mapeamento Objeto Relacional refere-se a técnica de mapear os registro do
Banco de Dados em objetos e persistir as informações contidas nos objeto em forma de
linhas e colunas.
Como o próprio nome diz, Mapeamento Objeto / Relacional,é responsável por
mapear classes e atributos do modelo orientado a objeto para tabelas e colunas do banco de
dados.
Existem várias formas de fazer esse mapeamento. Alguns frameworks utilizam a
linguagem XML, outros nos obrigam a implementar alguma Interface ou trabalhar com os
Atributos do .NET, mas o objetivo é sempre o mesmo: Permitir que o framework consiga
gerar os comandos SQL dinamicamente.
Uma outra característica deste modelo é a independência do banco de dados.
Devido a geração de comandos dinâmicos, o framework pode analisar qual banco de dados
a aplicação está acessando e gerar os comandos no dialeto específico do banco de dados, ou
seja, é possível mudar o banco de dados da aplicação apenas alterando um arquivo de
configuração.
3.1. Mapeando objetos para tabelas
Para permitir a correta persistência de objetos em um banco de dados relacional,
algum acordo deve ser feito no tocante à forma como os dados serão armazenados. 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, de forma que o desenvolvedor de software, ao escolher o
mecanismo de persistência com o qual trabalhará, sabe como deve organizar as tabelas em
seu banco de dados para suportar o esquema de objetos desejado. No decorrer deste artigo,
detalhamos como é feito o mapeamento de cada um dos elementos de um objeto: seus
atributos, relacionamentos e classes descendentes (herança).
22
3.2. 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 dos dados (alguns SGBDs podem não suportar tipos binários
longos, por exemplo) e o comprimento máximo dos campos (no caso de números e strings).
Também é importante lembrar que, em diversos casos, atributos de um objeto não devem
ter obrigatoriamente uma coluna em uma tabela que os referencie. Como exemplo,
podemos citar o valor total de um pedido: este dado poderia ser armazenado no objeto para
fins de consulta, mas mantê-lo no banco de dados talvez não seja uma idéia tão
interessante, por tratar-se de um valor que pode ser obtido através de consultas. Além
disso, existem casos onde um atributo pode ser mapeado para diversas colunas (exemplos
incluem endereços completos, nome dividido em 'primeiro nome' e 'sobrenome' no banco
de dados) ou vários atributos podem ser mapeados para uma mesma coluna (prefixo e
número
de
telefone,
por
exemplo).
As
implementações
de
Camadas
de
Persistência provêem, em alguns casos, suporte a este tipo de situação.
3.3. Mapeamento de classes em tabelas
O mapeamento de estruturas de classes em tabelas de uma base de dados relacional
nem sempre é um processo simples: enquanto alguns acham interessante a adoção de
"tabelões" (isto é, tabelas não-normalizadas agrupando dados de diversas entidades) como
repositório para os dados, outros preferem ater-se às regras propostas pelas teorias de
normalização de bancos de dados relacionais. As três técnicas de mapeamento de objetos
mais comumente implementadas (inclusive em Camadas de Persistência) são detalhadas a
seguir. É comum a adoção de uma destas técnicas, mesmo quando nenhum tipo de
mecanismo de persistência automático é adotado no desenvolvimento.
3.4. Mapeamento de uma tabela por hierarquia
23
Segundo esta estratégia, toda a hierarquia de classes deve ser representada por uma
mesma tabela no banco de dados: uma coluna que identifique o tipo do objeto serve para
identificar a classe do objeto representado por cada linha na tabela, quando nenhum outro
modo de identificação é viável. As desvantagens desta estratégia são evidentes: a ausência
de normalização dos dados fere as regras comuns da teoria de modelagem de dados – além
disso, para hierarquias de classes com muitas especializações, a proliferação de campos
com valores nulos na maioria das linhas da tabela se torna também um problema potencial.
3.5. Mapeamento de uma tabela por classe concreta
Nesta estratégia, teremos uma tabela no banco de dados 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 adotado na estratégia
anterior. A estratégia de geração de uma tabela para cada classe concreta leva
à redundância de dados: quaisquer atributos definidos em uma superclasse abstrata na
hierarquia devem ser criados em todas as tabelas que representam subclasses da mesma.
Além disso, mudar o tipo (especializar ou generalizar) 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.
3.6. Mapeamento de uma tabela por classe
Na terceira estratégia proposta, 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 modalidade de mapeamento, 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. Esta é a
técnica que mais naturalmente mapeia objetos para bancos de dados relacionais, de forma
24
que as Camadas de Persistência geralmente forçam a utilização de um esquema de dados
que siga esta modalidade de mapeamento. A quantidade de junções (joins) entre tabelas
para obter todos os dados de um objeto o seu principal ponto negativo.
A tabela 1 faz um comparativo destas três técnicas quanto à facilidade de consulta a
dados interativa (ad-hoc reporting), facilidade implementação, facilidade de acesso aos
dados, acoplamento dos dados das classes mapeadas, velocidade de acesso e suporte a
polimorfismo.
Uma tabela por
hierarquia de classes
Uma tabela por
classe concreta
Uma tabela por
classe
Ad-hoc reporting
Simples
Médio
Médio/Difícil
Facilidade de
implementação
Simples
Médio
Difícil
Facilidade de acesso Simples
Simples
Médio/Simples
Acoplamento
Alto
Baixo
Velocidade de acesso Rápido
Rápido
Médio/Rápido
Suporte a
polimorfismos
Baixo
Alto
Muito alto
Médio
Tabela 1. Comparativo entre técnicas de mapeamento de classes
3.7. Mapeamento de relacionamentos
Os relacionamentos de associação entre objetos são uma das características mais
facilmente mapeadas. Conceitualmente, existem apenas trás tipos de relacionamentos
possíveis (um-para-um, um-para-muitos e muitos-para-muitos).
Relacionamentos um-para-um necessitam que uma chave (foreign key) seja posta
em uma das duas tabelas, relacionando o elemento associado na outra tabela. Dependendo
da disposição desta chave estrangeira, podemos definir a navegabilidade do relacionamento
(que se dá sempre da tabela que possui a chave estrangeira para a tabela referenciada). 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 (lado
25
"n" do relacionamento).No caso de relacionamentos muitos-para-muitos (ou n-para-n),
convenciona-se criar uma tabela intermediária que armazene pares de chaves, identificando
os dois lados do relacionamento.
Há uma tendência para a utilização de Linguagens de Programação Orientadas a
Objeto(LPOO) e mecanismos de persistência diversos, principalmente, Banco de Dados
Relacionais(BDR).Surge então um problema, a integração entre a linguagem e o BD.
Embora existam várias APIs e modelos de mapeamento que possibilitam esta integração,
elas devem ser utilizadas de acordo com diretrizes para que não se perca os benefícios da
orientação a objetos e nem do BDR.
O simples mapeamento das classes, em nível de projeto, para tabelas do BDR não
garante a resolução do problema, na verdade existem outros aspectos, não menos
importantes, que podem levar a, violação dos princípios básicos da orientação a objetos
como encapsula-mento e modularização, ou descaracterização da arquitetura adotada.
Mesmo assim o modelo de objetos do banco é diferente do modelo de objetos utilizado pela
linguagem de programação. Enquanto a linguagem trabalha com objetos na memória, o
banco trabalha com objetos em disco, o que exige algoritmos e estratégias diferenciadas.
Além de que, os BDOOs não são, atualmente, a tecnologia padrão no mercado, por conta
do legado em investimento em sistemas desenvolvidos, pela cultura dos profissionais
atuando no mercado ou até mesmo por questões de performance.
Algumas ferramentas RAD, como DelphiTM, JbuilderTM e Dreamweaver, tornam
semi-automática a integração de uma LPOO com um BDR. No entanto essa implementação
é realizada sem a preocupação de critérios que garantam a continuidade e reversibilidade da
implementação em relação ao projeto. Estes erros não são somente cometidos nestas
condições, existem diversas “implementações ad hoc” que infringem estes e outros
aspectos.
Um mecanismo de persistência tem três componentes básicos, são eles:
– Regras
– API
de mapeamento;
de acesso ao Banco de Dados;
– Linguagem
de consulta;
26
Este componentes se interligam para gerar o mecanismo de persistência, que deve
ter como objetivos a maior abstração possível do BDR nas regras de negócio e a melhor
performance possível. Além disso um mecanismo deve considerar também as
funcionalidades tanto da LPOO quanto do BDR, resultando maior reusabilidade,
extensibilidade e eficiência.
No caso de se obter reusabilidade nas regras de negócio orientado a objeto é preciso
seguir o princípio da independência dos objetos de negócio em relação ao mecanismo de
persistência.
As regras de mapeamento gerenciam como o modelo OO que é mais rico
semanticamente, vai ser mapeado para o modelo relacional. Como irão se comportar
herança, agregação, entre outros devem estar definidos nestas regras.
A linguagem de consulta é responsável para manipular os dados do banco, pode ser
baseada em objetos ou não.
As APIs são responsáveis pela integração do mecanismo com as regras de negócio.
Estas podem ser intrusivas ou não. Quando estas impõem regras sob criação das classes
persistentes, a API é intrusiva, caso contrário não.
A tendência é que as APIs sejam não intrusivas, porém dificilmente o BD será
utilizado com eficiência, bem como os conceitos transparentes ao Banco de Dados, como
transações, serão mais difíceis de implementar sem modificar ou denegrir responsabilidades
no modelo de objetos.
Em termos de performance deve-se desenvolver uma política sobre como e quais os
atributos serão carregados, em uma consulta. Podemos utilizar o carregamento antecipado,
ou o carregamento tardio4. Em alguns casos deve-se ter uma política de escrita no BD
também.
3.2. MAPEAMENTO OO – ER
A integração objeto-relacional requer uma estratégia para mapeamento de modelo
objeto para o modelo relacional. O banco de dados relacional possui características
importantes tais como consultas rápidas, compartilhamento de informações entre programas
e armazenamento permanente. O modelo objeto possui componentes, estado e é baseado
27
em estruturas que combinam código e dados e ainda possuem sua própria identidade
(Object Identification – OID) Esta identidade não depende dos valores que o objeto possui,
ela possibilita estabelecer referências entre objetos e definir os relacionamentos, os quais
podem ser dos seguintes tipos: associação, agregação, generalização/especialização.
OIDs possibilitam simplificar a estratégia de uso de chaves no banco de dados, eles
facilitam a navegação entre objetos simplificados os joins. Outra vantagem é que o uso de
OIDs facilitam a manutenção dos relacionamentos entre objetos. Quando todas as tabelas
possuem suas chaves baseadas num mesmo tipo de colunas, torna-se mais fácil escrever o
código e tirar vantagens disso.
O modelo objeto e o modelo relacional são fundamentalmente diferentes, o modelo
objeto é útil para expressar relações complexas entre objetos nas Linguagens de
Programação Orientada a Objeto como, C++ e Java. Já o modelo relacional é útil para
gerenciar grande volume de dados em Banco de Dados Relacional como, SQL Server e
Oracle.
Sendo assim cada uma das classes do modelo OO é transformada em uma tabela no
modelo relacional. Para as associações, o mapeamento é feito ou com a criação de novas
tabelas ou com a cópia das chaves de uma tabela para outra. Para agregação, a regra é
generalização /especialização, cuja transformação para o modelo relacional é executado
com interação do usuário, uma vez que para um mesmo caso pode haver diferentes formas
de transformação.
Existem algumas regras de transformação que tem por finalidade a conversão do modelo
OO para o modelo relacional. As regras se dividem em (OBJECTMATTER, 2003):
- Mapeamento Básico
- Mapeamento Herança de Classe
- Mapeamento Relacionamento de Objeto
Serão descritas as técnicas fundamentais requeridas para o sucesso do mapeamento
objeto para o relacional. Isto poderá ajudar a rever os conteúdos que predominam no
desenvolvimento e praticas que envolvem este assunto
3.2.1. Mapeamento Básico
28
A classe pode ser mapeada para tabelas. O simples mapeamento entre a classe
persiste e a tabela é um-para-um. Neste caso, todos os atributos da classe persistente são
representados por todas as colunas da tabela. Cada instância da classe do negócio é
armazenada em uma linha da tabela.
Um atributo de uma classe pode ser mapeado para zero ou mais colunas. È
importante lembrar que nem todos os atributos são persistentes, por exemplo, um atributo
total que é usado para instanciar um somatório, logo, este não é persistido no banco de
dados. Alguns atributos dos objetos são objetos por si só, como exemplo: endereço, cep,
rua,etc.. portanto devem ser tratados como relacionamentos.
3.2.2. Mapeamento herança de classe
O conceito de herança lança vários interesses do entrelaçamento de salvar objetos
em banco de dados relacionais. Este assunto basicamente concentra-se em imaginar como
organizar o atributo herdado em seu modelo persistente. A maneira como será resolvido
este desafio poderá ter um grande impacto no projeto de seu sistema.
Existem três tipos de soluções que são fundamentais para mapear a herança para o modelo
relacional:
- Mapeamento uma tabela para toda hierarquia: com este método mapeia-se toda a
classe de herança para uma tabela, onde todos os atributos de toda a classe da hierarquia
são armazenados e, uma coluna OID é introduzida como chave primária na tabela;
- Mapeando uma tabela por classe: com este método cada tabela inclui tanto os seus
atributos quanto os atributos herdados. Somente as classes “folhas” das hierarquias são
mapeadas para tabelas;
-Mapeando uma tabela por classe; com este método cria-se uma tabela por classe. A
principal vantagem é que esta abordagem é a que esta mais conforme a orientação a
objetos. Os registros estão armazenados nas tabelas apropriadas, de acordo com seus
29
papeis. Uma desvantagem, neste método são mais tabelas BD (mais tabelas para manter os
relacionamentos).
3.2.3. Mapeando relacionamento de objetos
Não somente devemos mapear os objetos para o banco de dados, mas também
mapear os relacionamentos que envolvem os objetos. Existem quatro tipos de
relacionamento os quais os objetos podem estar envolvidos: generalização, associação,
agregação e composição. Para mapear efetivamente esses relacionamentos, devemos
estender as diferenças entre eles, como implementar a generalização, e como implementar
relacionamento muitos-para-muitos especificamente.
Para o banco de dados, perspectivamente, somente tem diferença entre os
relacionamentos de associação e agregação/composição e como o objeto é firmemente
amarrado a ele. Com a agregação e composição qualquer coisa que você faça com o todo
no banco de dados você sempre precisará fazer nas partes, enquanto que com associação
este não é o caso.
O diagrama de classes é a estrutura das tabelas que são usadas para discutir as varias
Maneiras de relacionamentos de objetos um-para-um, um-para-muitos, muitos-paramuitos, que podem ser mapeados para o modelo relacional.
No modelo relacional o relacionamento um-para-um, mantém-se, comumente, por
meios de colunas de chaves estrangeiras. Esta coluna de chave estrangeira mantém o valor
da chave primária (OID do objeto) da coluna (objeto) referenciada. O relacionamento umpara-um pode ser definido como referencia ao atributo, isto pode ser feito
transparentemente convertendo a chave estrangeira do objeto referenciado. Isto também
possibilita a definição do relacionamento um-para-um no modelo relacional usando uma
join table.
No modelo objetos existem dois tipos de relacionamento um-para-muitos:
agregação (parte de), e associação. Um relacionamento de agregação é definido por meios
do próprio atributo e um relacionamento de associação por meios de uma coleção
referenciada ao atributo. A diferença entre as duas é que no relacionamento próprio, que é
próprio é atualizado no banco de dados. Todos os objetos, em todas as suas coleções, são
30
automaticamente atualizados (este comportamento pode ser modificado em tempo de
execução se necessário).
No modelo relacional, um-para-muitos pode ser definidos usando a coluna de chave
estrangeira ou usando uma join table é uma tabela com o propósito de armazenar os valores
mapeados entre a chave estrangeira das duas tabelas envolvidas no relacionamento.
Um relacionamento muitos-para-muitos pode ser como uma bi-direcional
associação um-para-muitos. Para criar este tipo de relacionamento, simplesmente,
definimos o atributo referenciado na coleção, em cada classe envolvida no relacionamento.
No modelo relacional um relacionamento muitos-para-mutos pode ser definido usando
colunas de chaves estrangeiras ou uma join table.
Para usar chaves estrangeiras, uma coluna com a chave estrangeira é definida para
cada tabela envolvida no relacionamento. Cada coluna da chave estrangeira mantém a
chave da outra tabela. Existem vários tipos que podem ser implementados para associação
muitos-para-muitos usando join table.
Uma das maneiras de implementar relacionamento muitos-para-muitos é usando
uma tabela associativa. O nome da tabela associativa é geralmente a combinação dos nomes
das tabelas que estão associadas ou o nome da associação implementada entre elas. A
associação original possui um relacionamento muitos-para-muitos e com a utilização da
tabela associativa se faz um cruzamento das multiplicações e não se perde essa associação.
O propósito de uma tabela associativa é implementar uma associação descrita em uma
classe associativa.
3.3. OBJETIVOS GERAIS DOS COMPONENTES MAPEAMENTO OO- ER
Mapeamento objeto-relacional é a transformação das regras do modelo objeto para
as regras e normalizações do modelo relacional. Isto significa que existem dois modelos
utilizados na construção de sistemas. O modelo objeto é utilizado para descrever e
apresentar as regras de negócio, enquanto. A utilização do paradigma relacional na
persistência dos objetos, atividade importante para definir a base de dados de sistemas que
utilizam o paradigma orientado a objeto no seu desenvolvimento.
31
A transformação de uma classe em uma tabela e seus respectivos relacionamentos e
atributos em chaves primárias e chaves estrangeiras define, com mais precisão, questões
relacionadas à performance no acesso aos dados (não fazer um-para-um). O relacionamento
um-para-um, possui algumas desvantagens:
1º são mais tabelas no Banco de Dados (mais tabelas para manter os relacionamentos);
2º o tempo de leitura e escrita de dados será maior usando esta técnica porque varias tabelas
serão acessadas. Neste caso, ceve existir a preocupação em atualizar e manipular as classes
de nível superior na hierarquias profundas, são requeridos múltiplos joins para recuperar as
informações básicas do objeto.
Resumindo, o mapeamento OO-ER é importante para que dois mundos
consolidados (OO – desenvolvimento e ER – base de dados) possam convergir em uma
única solução.
O objetivo principal do componente é fazer o mapeamento objeto-relacional
utilizando como entrada um arquivo XMI que é o padrão entre intercambio de dados
utilizado hoje, e gerando como saída um arquivo .sql para utilização no banco de dados.
3.3.1. Mapeamento básico
Cada classe do modelo será transformada em uma tabela do modelo relacional e,
todos os seus atributos, serão representados por todas as colunas da tabelas. A Figura 1
apresenta um exemplo de mapeamento básico.
A chave primária é definida através da utilização de Object Identification (OID).
32
FIGURA 1 – Mapeamento Básico
Geração das Primary Keys (OID): Toda a classe transformada m tabela tem um
identificador próprio: o seu OID. O qual facilita o relacionamento entre as tabelas, e com
isso, as consultas na maioria dos casos se tornam mais eficientes.
3.3.2. Mapeamento de relacionamentos
•
Um-para-um
- O mapeamento de relacionamentos um-para-um foi implementado no componente
utilizando a chave primária da tabela relacionada como chave estrangeira. A figura
2 apresenta um exemplo de mapeamento um-para-um.
- Coloca-se a chave primaria de uma tabela como chave estrangeira na outra tabela,
independentemente do lado do relacionamento.
FIGURA 2 – Mapeamento de relacionamentos (um-para-um)
•
Um-para-muitos
33
- O mapeamento de relacionamentos um-para-muitos foi implementado no
componente utilizando a chave primária da tabela relacionada como chave
estrangeira da tabela referenciada. A figura 3 apresenta um exemplo de mapeamento
um-para-muitos.
- Coloca-se a chave primária de uma tabela como chave na outra tabela, no lado do
muitos no relacionamento.
FIGURA 3 – Mapeamento de relacionamentos (um-para-muitos)
•
Muitos-para-muitos
- O mapeamento de relacionamento muitos-para-muitos foi implementado no
componente utilizando uma tabela associativa. A figura 4 apresenta um exemplo de
mapeamento muitos-para-muitos.
- Cria-se uma terceira tabela que possui as chaves primárias das duas tabelas
relacionadas, como chaves estrangeiras na tabela associativa. O nome da nova
tabela é a junção dos nomes das tabelas relacionadas.
34
FIGURA 4 – Mapeamento de relacionamentos (muitos-para-muitos)
3.3.3. Generalização
A generalização pode ser mapeada de três formas: uma tabela para toda hierarquia,
uma tabela por classe concreta e uma tabela por classe. O componente contempla as três
formas de mapeamento. Previamente pode-se definir um mapeamento padrão para a
generalização (uso do properties) ou através da escolha, pelo usuário, em tempo de
execução. As três formas são:
•
Uma tabela para toda hierarquia: a tabela pai herda os atributos das tabelas filhas, e
permanece o nome da tabela pai, conforme apresenta a figura 5;
35
FIGURA 5 – Uma tabela para toda hierarquia
•
Uma tabela por classe concreta: as tabelas filhas herdam os atributos da tabela pai e
permanecem com os seus nomes. A tabela pai é excluída, conforme apresentado na
figura 6;
FIGURA 6 – Uma tabela por classe concreta
•
Uma tabela por classe: para cada classe se gera uma tabela com os seus respectivos
atributos, transformados em colunas e, a herança é representada com um
relacionamento um-para-um entre o pai e seus filhos. Conforme apresentado na
figura 7.
36
FIGURA 7 – Uma tabela por classe
37
3.4. REPRESENTAÇÃO DE UM ESQUEMA RELACIONAL
Para que possamos aplicar as regras de mapeamento, consideramos que as relações
no esquema relacional de origem estão no mínimo na 2ª forma normal, ou seja, as relações
podem conter dependência funcionais transitivas, mas não dependências parciais.
Consideramos também que, são conhecidas as chaves primárias e estrangeiras das relações
que compõe o esquema relacional de origem.
Em relação ao particionamento horizontal de tabelas, consideramos que se tal
particionamento existe no esquema relacional de origem, isto foi feito devido á decisão de
projeto. Por não conhecemos o motivo de tal decisão, estabelecemos que: se duas tabelas A
e B são equivalentes, mas são tratadas como tabelas diferentes devido ao particionamento
horizontal, então estas tabelas continuando sendo tratadas como distintas no esquema
objeto-relacional, observamos que, não estamos considerando o problema de performance
durante o mapeamento, uma vez que a tecnologia objeto-relacional ainda é recente, e não se
pode afirmar, deste ponto de vista, qual a melhor estrutura para modelar os dados, diante
das diversas possibilidades oferecidas. Entretanto, procuramos fazer o mapeamento
oferecendo uma melhor modelagem do ponto de vista de compreensão do esquema.
Ainda com o objetivo de minimizar as “falhas” de projeto no esquema relacional de
entrada, definimos, a seguir, um procedimento de pré-processamento deste esquema,
descrito por;
1. Sejam as relações R1 e R2. Se a chave primária de R1 é também uma chave estrangeira
que referencia a chave primária de R2 e a chave primária de R2 é também uma chave
estrangeira que referencia a chave primária de R1, então dizemos que as relações R1 e R2
estão particionadas verticalmente. Neste caso, consideramos que as relações R1 e R2
representam uma única relação (R3), cujos atributos dão dados pela união dos atributos de
R1 e R2. A chave primária de R3 é a mesma de R1 ( que é a mesma chave primária de R2).
Consideramos também, que todas as restrições definidas sobre R1 e R2, tornam-se
38
restrições sobre R3, exceto as restrições de chave estrangeira de R1 para R2 e vice-versa. A
relação R3 é então definida: R3 (A, B, C, D, E).
A opção de “unir” tabelas particionadas verticalmente deve-se ao seguinte fato: se,
aplicando as regras de mapeamento tais tabelas fossem mapeadas em tabelas de objetos
distintas, que possuem em identificadores únicos. Desta forma, mesmo que dois objetos
possuem a mesma chave-primária, mas em tabelas distintas, então estes objetos possuem
OIds diferentes.
Conforme definido anteriormente, consideramos que as relações do esquema
relacional de entrada podem estar na 2ª forma normal. Desta forma, utilizamos o processo
descrito para relações, de tal forma que se na 3ª forma normal.
3.4.1. Algumas regras para mapeamento
As regras listadas a seguir, para mapear um esquema relacional em objetorelacional, são baseadas em um conjunto trabalho, com algumas adaptações para adequar o
modelo objeto-relacional descrito. A maioria destes trabalhos tratam do mapeamento para
estruturas orientadas a objetos.
Regra T1: Toda tabela em um esquema relacional que tem somente um atributo na chave
primária correspondente a uma tabela de objeto-relacional. Os atributos da tabela relacional
tornam-se atributos do tipo de dados estruturados que definem a tabela de objetos.
Regra T2: Se a chave primária de uma tabela no esquema relacional tem mais que um
atributo, e além disso, a tabela possua outros atributos que não pertençam à chave primária,
então esta tabela relacional corresponde a uma tabela de objetos no esquema objetorelacional. Os atributos de chave primária que representam chaves estrangeiras, devem ser
mapeados como uma referência ao tipo que define a estrutura de tabelas referenciada pela
chave estrangeira.
39
Regra T3: Toda tabela em um esquema relacional, cuja chave primária é composta por mais
de um atributo, e todos eles representam chaves estrangeiras. Esta associação é
representada no esquema objeto-relacional através de tabelas aninhadas.
Regra T4: Toda tabela que possui atributos de chave estrangeira que não fazem parte da
chave-primária, estabelece uma associação entre esta tabela e a cada tabela referenciada por
chaves estrangeiras. Esta associação é representada no esquema objeto-relacional através de
referências (tipo de dados ref.).
Regra T5: Todo par de tabelas R1 e R2 que têm as mesmas chaves primárias podem ser
envolvidos em um relacionamento de herança. O relacionamento R1 “ é um “ R2 existe se a
chave primária de R1 é também a chave estrangeira que refere a tabela R2.
Regra T6: Quando um relacionamento de herança esta embutido em uma única tabela , é
necessário extrair regras do banco de dados de uma maneira não convencional. Existem
vários algoritmos que podem identificar tais regras, denominadas “strong rules” em banco
de dados relacionais. Usando-se algum deste algoritmo, pode-se identificar as condições
que são estabelecidas para que um atributo, ou conjunto de atributos, possua valor nulo.
Identificadas estas regras, podem-se extrair da tabela os relacionamentos de herança nela
embutidos. Utilizamos o algoritmo descrito para determinar relacionamento de herança, em
um esquema relacional.
Regra T7: Seja R uma tabela cuja chave primária tem mais que um atributo e no mínimo
um deles não é chave estrangeira, e além disso, R não possui outros atributos que não
pertençam à chave primária. Deve-se então acrescentar um atributo na tabela referenciada
pela chave primária, cujo domínio é um tipo coleção formado pelos atributos de tabelas,
exceto o atributo de chave estrangeira.
40
4. FERRAMENTAS DE MAPEAMENTO OBJETO RELACIONAL
4.1. FUNCIONALIDADES ESSENCIAIS DE UMA FERRAMENTA DE
MAPEAMENTO OBJETO/RELACIONAL
Já existem hoje no mercado algumas ferramentas capazes de realizar mapeamento
entre objeto e registros de tabelas de banco de dados relacionais, fazendo com que
operações básicas de cadastro como armazenamento, atualizações, exclusão e consultas
possam ser realizadas naturalmente sobre objeto e reflitam por fim em SQL que serão
interpretados pelos bancos relacionais.
Produtos que realizam mapeamento objeto – relacional integram as capacidades das
linguagens de programação orientadas a objeto com sistemas de gerenciamento de bancos
relacionais, como Oracle, DB2, Sysbase, e outros. Produtos que realizam mapeamento
objeto-relacional são projetados para funcionar bem como linguagens de programação
orientadas a objetos.
A definição de correspondência entre elementos modelados em objeto e elementos
no modelo relacional precisa ser de fáceis configurações. Um grande numero de soluções
usa arquivos de configurações XML. Um editor gráfico para edição destes arquivos
consiste em uma vantagem extra e terá melhor preferência pela equipe projetista de
software para momento de realizar-se uma integração da ferramenta ao ambiente de
desenvolvimento.
Ferramentas de mapeamento podem ser mais ou menos genéricas e sua capacidade
de adaptar-se aos recursos existentes é um importante motivo de escolha. Buscar
informações armazenadas em banco de dados relacionais pode prover muita complexidade
se a ferramenta de mapeamento por em uso controle de restrições de integridade sobre o
modelo relacional usado.
A ferramenta deve prover funcionalidade de pesquisa, de montagem de objeto e de
atualizações. Estes mecanismos precisam gerenciar corretamente quaisquer problemas de
transações e adaptar-se a arquitetura do sistema de informações ao qual se destina, mesmo
que seja usado um servidor de aplicação.
41
A descrição dos tipos de dados varia de um modelo para o outro. A ferramenta de
mapeamento deve estar apta a diferenciar os tipos de dados e propor correspondência no
modelo relacional do banco de dados utilizados.
A partir do momento em que uma empresa desenvolvedora de software opta por uma
ferramenta de mapeamento objeto/relacional ela geralmente tem como objetivo conseguir
as seguintes vantagens no seu processo de engenharia de software:
•
Melhorar a manutenibilidade de código. O código relacionado a persistência esta
embutido em um modulo especifico e não é modificado durante os vários ciclos de
vida do desenvolvimento;
•
O tamanho de código médio seja reduzido uma vez que o código relacionado a
persistência é inteiramente genérico e não esteja distribuído por toda a aplicação
•
O trabalho do(a) desenvolvedor(a) seja facilitado e reduzido, uma vez que ele não
tenha mais que com problemas de persistência e possa concentrar-se em resolver
problemas de persistência e possa concentrar-se em resolver problemas de regras de
negócios;
•
Aumentar a portabilidade da aplicação: numerosos frameworks de mapeamento
objeto/relacional habilitam o uso da maioria dos bancos de dados relacionais
existentes no mercado para serem usados transparentemente;
A tabela abaixo exibe uma lista com os principais itens que devem ser verificados ao
adquirir uma ferramenta de mapeamento objeto/relacional;
Características
Descrição
Herança
A ferramenta de desenvolvimento deve ser hábil a projetar um
modelo de herança de objetos via relacionamento de tabelas em
um banco de dados relacional.
Representar relacionamento entre objetos (1-1, 1-M, M-M)
Deve saber como projetar relacionamento entre objetos em
bancos relacionais.
Número de BDs Relacionais suportados
Possui suporte aos principais BDs. Relacionais (DB2, Oracle,
Informix, MSSQLServer, entre outros)
Mecanismos de otimização de performance
Quais as funcionalidades para otimizar a performance da
ferramentas? Instanciação parcial de objetos, cachê em memória
para leitura, etc.
42
Transações Simples
A ferramenta suporta o modelo transacional dos BDs
Relacionais?
Suporte e transações aninhadas
A ferramenta suporta o modelo de transações aninhadas BDs
Relacionais?
4.2. TIPOS DE FERRAMENTAS
4.2.1. Hibernate
É um dos mais bem sucedidos projetos Open Source desenvolvido em Java. Sua
facilidade de uso, abstração e transparência fizeram dele quase um padrão em frameworks
de mapeamento objeto-relacional. Embora sua documentação seja rica e extensa, a falta de
exemplos e dicas em português muitas vezes dificulta
a sua adoção por parte de
desenvolvedores brasileiros. O Hibernate não trata apenas do mapeamento de classes Java
em tabelas de banco de dados (e tipos de dados Java em tipo de dados SQL), mas
disponibiliza também um poderoso mecanismo de consulta de dados que pode reduzir
significamente o tempo de desenvolvimento. O Hibernate objetiva “liberar” o
desenvolvedor em 95% das tarefas comum relacionadas à programação de persistência de
dados.
O Hibernate disponibiliza um poderoso framework para persistência objetorelacional, de fácil manuseio e alto desempenho. O Hibernate provê suporte para coleções
e relacionamentos entre objetos, assim como herança. polimorfismo e composições. Ele
também tem um rica linguagem de consulta orientada a objetos, o HQL ( Hibernate Query
Language) para recuperação de objetos de banco de dados, uma camada de cachê eficiente
e suporte para Java Management Extensions (JMX).
O Hibernate possui uma comunidade ativa de usuários que ajuda a prover suporte e
ferramentas para extensão. È distribuído de acordo com a Lesser GNU Public License
(LGPL), portanto pode ser usado tanto em explicações de código aberto como comerciais.
Suporta um grande numero de banco de dados, incluindo Oracle e DB2, assim como banco
de dados livres tais como de dados, PostgreSQL e MySQL, permitindo a utilização de
meios nativos para a geração de chaves primarias e pessimistic locking além de resolver
problemas como pool de conexões e configurações de datasources.
Arquitetura
43
Uma visão de alto nível da arquitetura do Hibernate:
Figura 8: Uma visão simplista do Hibernate
Esta figura mostra o Hibernate usando o banco de dados e a configuração
de dados para disponibilizar serviço de persistência (e objetos persistentes) para a
aplicação.
Dependendo da complexidade do projeto, um maior número de APIs e componentes
são utilizados pelo Hibernate. A figura abaixo exibe uma visão mais detalhada da
arquitetura:
Figura 9: Componentes do Hibernate
44
De acordo com a especificação do Hibernate, estes componentes são definidos da seguinte
forma:
• SessionFactory (net. sf. hibernate. SessionFactory) Armazena os mapeamentos e
configurações compiladas para um banco de dados. É uma fábrica de objetos Session e
que provê conexões através do ConnectionProvider. Este objeto é imutável e threadsafe
(pode ser acessado por múltiplas threads sem perigo de inconsistência).
• Session (net. sf. Hibernate .Session) Um objeto que representa o "diálogo" entre a
aplicação e a persistência (banco de dados), encapsulando uma conexão JDBC e
manipulado por somente uma thread. Este objeto controla um cache dos objetos
persistentes. Os Session não devem durar toda a execução da aplicação, ou seja, são
objetos de "vida curta".
• Persistent Objects and Collections Objetos manipulados por somente uma thread
que contém as informações persistentes e regras de negócio. Devem ser JavaBeans
(possuir m construtor sem parâmetros e métodos get/set para os atributos persistidos).
Eles só podem estar associados à exatamente uma Session. Assim que o Session é
finalizado, estes objetos são liberados para serem usados em qualquer
camada da
aplicação.
• Transient Objects and Collections
Instâncias de classes persistentes que não estão atualmente associadas a um Session.
Podem ter sido instanciados pela aplicação mas ainda não persistidos, ou eles podem ter
sido instanciados por um Session fechado.
• Transaction (net.sf.hibernate.Transaction) Objeto
usado
pela aplicação
para
especificar unidades atômicas de acesso ao banco de dados. Não deve ser manipulado
por múltiplas threads. Abstrai a aplicação dos detalhes das transações JDBC, JTA ou
CORBA. Em alguns casos um Session pode manipular vários Transactions.
45
• ConnectionProvider (net.sf.hibernate.connection.ConnectionProvider) Uma fábrica
para (e pool de) conexões JDBC. Abstrai a aplicação dos detalhes do Datasource ou
DriverManager. Não exposto à aplicação, mas pode ser estendido/implementado pelo
desenvolvedor.
• TransactionFactory (net.sf.hibernate.TransactionFactory)Uma fábrica para instâncias de
Transaction. Não exposto à aplicação, mas pode ser estendido/implementado pelo
desenvolvedor.
4.2.2. Instalação e Configuração
A última versão Hibernate pode ser copiada do
siteoficial(http://www.hibernate.org). A instalação é simples, bastando descompactar o
arquivo .zip. O diretório criado contém o JAR núcleo do Hibernate (hibernate2.jar).
Também existe um subdiretório chamado lib onde ficam os JARs das outras
APIs utilizadas pelo framework.
Esses arquivos JARs devem ser referenciados no classpath da aplicação. É também
necessário que a classe de driver do seu banco de dados esteja no classpath.
O Hibernate foi desenvolvido para operar em vários ambientes diferentes. Por
isso, existe um grande número de parâmetros de configuração. Por outro lado, a
grande maioria dos parâmetros tem valor padrão e, além disso, o Hibernate é distribuído
com um arquivo chamado hibernate.properties que mostra as várias opções disponíveis.
Você apenas precisa colocar esse arquivo no classpath e customizá-lo.
Configurando o SessionFactory
A tabela abaixo mostra os parâmetros mais importantes para configuração da
conexão JDBC (com banco de dados):
46
Tabela 2: Parâmetros principais para configuração da conexão JDBC
O Hibernate possui ainda uma série de parâmetros que definem seu comportamento
em tempo de execução, como por exemplo:
Tabela 3: Parâmetros que definem comportamento em tempo de execução
O Hibernate trabalha com dialetos para um grande número de bancos de dados, tais
como: DB2, MySQL, Oracle, Sybase, Progress, PostgreSQL, Microsoft SQL Server,
Ingres, Informix entre outros.
Todos estes parâmetros são usados na configuração inicial. A partir deles, é criado um
objeto da classe SessionFactory. Este objeto contém todas as informações passadas na
configuração. Quando criado, o SessionFactory carrega e verifica todas as configurações.
Esta operação consome mais tempo do Hibernate e por isso é recomendável criar este
objeto somente uma vez e utilizá-lo durante toda execução da aplicação. O Hibernate
permite que sejam criados mais de um SessionFactory, mas isso só deve ser feito se houver
necessidade de acesso a mais de um banco de dados. Existem duas formas de informar ao
SessionFactory as configurações do Hibernate:
4.2.3. Configuração através do objeto Configuration
A
configuração
pode
ser
feita
através
da
classe
Configuration
(net.sf.hibernate.cfg.Configuration). Os objetos desta classe podem ser instanciados de
forma direta. Configuration deve receber as configurações gerais através da classe
Properties e também os mapeamentos Objeto/Relacional (ORM).
Na tabela 4 um exemplo de como criar um SessionFactory através do objeto
Configuration:
Tabela Exemplo 4: Criando um SessionFactory através do objeto Configuration
47
4.2.4. Mapeamento O/R Básico
O Hibernate usa arquivos XML para mapear os atributos das classes em campos das
tabelas. Qualquer classe pode ser mapeada desde que seja um Bean, ou seja, possua um
construtor sem parâmetros e os atributos mapeados possuam métodos get e set. A presença
de um atributo de identificação (chave primária) é altamente recomendada, mas não é
obrigatória, caso contrário diversas funcionalidades não estarão disponíveis. Não é
necessário nenhum tipo de especialização (herança) ou realização de interface para que uma
classe seja persistida pelo Hibernate. Isto quer dizer que o Hibernate pode persistir objetos
POJO (Plain Old Java Object). Abaixo o exemplo de uma classe que poderia ser persistida:
Tabela Exemplo 5: Classe candidata à persistência
Abaixo, a implementação da classe do diagrama UML acima.
Tabela Exemplo 6: Implementação da classe Pessoa
Segue a declaração de criação da tabela que armazena a classe acima.
48
Aconselha-se escolher identificadores de tipos de grande capacidade (como
BIGINT) para que um grande número de registros possa ser armazenado. Nota-se o atributo
AUTO_INCREMENT para a chave primária idPessoa, logo em seguida fica claro o porquê
disso.
Tabela Exemplo 7: Declaração de criação da tabela que armazena a classe Pessoa
Para mapear uma classe numa tabela:
Tabela Exemplo 8: Mapeando a classe Pessoa numa tabela
Os arquivos de mapeamento são fáceis de configurar e divididos de maneira
bastante intuitiva. Além disso, a maioria dos parâmetros existentes possui valores padrão.
Este exemplo inicial exibe apenas os parâmetros obrigatórios (com exceção do elemento
<id>).
É obrigatório especificar o nome da classe e a tabela com a qual está associada. O
elemento <id> indica qual é o identificador do objeto e como ele é criado e obtido. Caso ele
seja omitido, o Hibernate considera que a classe não possui identificador. O atributo name
indica que atributo da classe será usado como identificador. O elemento generator informa
como serão gerados os identificadores dos novos elementos. Segue alguns dos tipos de
geradores existentes no Hibernate:
49
Tabela 9: Tipo de geradores existentes no Hibernate
Cada atributo da classe é mapeado através do elemento <property>. O único
parâmetro obrigatório é o nome do atributo. O Hibernate tentará encontrar uma coluna com
este mesmo nome e definir seu tipo por reflexão. Abaixo, o mapeamento desta mesma
classe com os parâmetros opcionais (usando o mapeamento simples para executar como
exemplo, nota-se que os nomes das colunas foram trocados).
Tabela Exemplo 10: Mapeamento da classe Pessoa com parâmetros opcionais
O primeiro parâmetro a notar é column. Ele indica a coluna correspondente ao
atributo da classe na tabela, caso não tenham o mesmo nome. O parâmetro type, indica o
50
tipo do atributo Hibernate. Abaixo a associação entre os tipos do Hibernate mais comuns,
as classes wrapper Java e os tipos no banco de dados:
Tabela 11: Associação entre tipos do Hibernate, classes wrapper Java e tipos no BD
No mapeamento, pode ser informado na propriedade type tanto o tipo Hibernate
quando a classe Java. Outra propriedade opcional importante é not-null.
Ela indica que no momento da persistência de um objeto, o determinado atributo
pode ser nulo ou não. Caso um atributo esteja indicado como not-null=”true” e no momento
do salvamento ele esteja null, Hibernate irá lançar uma exceção.
É essencial lembrar de incluir a linha abaixo no arquivo do hibernate.cfg.xml. Ela
indica que este mapeamento deve estar contemplado no SessionFactory.
Tabela Exemplo 12: Mapeamento contemplado no SessionFactory
51
4.3. Hibernate / Persistência
Hibernate é um mecanismo simples e poderoso que permite a persistência de
objetos em banco de dados relacionais de maneira transparente para qualquer tipo de
aplicação Java. Esta ferramenta, que pode ser baixada gratuitamente da Internet através do
endereço http://hibernate.sf.net/, possibilita que os objetos possam ser gravados e
recuperados a partir de um banco de dados sem que o desenvolvedor tenha que se
preocupar com muitos detalhes. Não há necessidade de se implementar mapeamentos hardcoded no código Java. O Hibernate resolve problemas como pool de conexões e
configurações de Datasources.
Em linhas gerais, a codificação de um sistema pode ser dividida em duas partes:
regras de negócio e serviços de infra-estrutura. Regras de negócio, como o próprio nome
diz, estão relacionadas ao negócio com o qual o sistema visa trabalhar. Já os serviços de
infra-estrutura estão relacionados à segurança, cache, transação, serviços de nomes, etc. A
idéia do Hibernate é permitir que o desenvolvedor mantenha seu foco sobre as regras de
negócio, liberando-o de parte das tarefas de infra-estrutura.
O Hibernate suporta alguns dos principais bancos de dados relacionais disponíveis
no mercado, permitindo a utilização de meios nativos para geração de chaves primárias e
pessimistic locking. O hibernate trabalha com os bancos de dados através de dialetos,
conforme a tabela 1.
Banco de dados
DB2
MySQL
SAPDB
Oracle
Sybase
Progress
McKoiSQL
Interbase/Firebird
Pointbase
PostgreSQL
HypersonicSQL
Microsoft SQL Server
Dialeto
cirus.hibernate.sql.DB2Dialect
cirus.hibernate.sql.MySqlDialect
cirus.hibernate.sql.SAPDBDialect
cirus.hibernate.sql.OracleDialect
cirus.hibernate.sql.SybaseDialect
cirus.hibernate.sql.ProgressDialect
cirus.hibernate.sql.McKoiDialect
cirus.hibernate.sql.InterbaseDialect
cirus.hibernate.sql.PointbaseDialect
cirus.hibernate.sql.PostgreSQLDialect
cirus.hibernate.sql.HSQLDialect
cirus.hibernate.sql.SybaseDialect
Tabela 13 – Bancos de dados suportados pelo Hibernate
52
Fonte: www.mundojava.com.br
O desenvolvimento usando Hibernate é um processo que pode ser dividido em cinco
etapas. O primeiro passo é a construção do banco de dados com o qual a aplicação irá
trabalhar, ou seja, criar as tabelas onde os objetos serão persistidos. Este banco de dados,
com suas entidades, atributos e relacionamentos, poderá ser criado de forma tradicional ou,
a critério do usuário, poderá ser utilizada a ferramenta SchemaExport que acompanha o
Hibernate, Esta ferramenta gera o esquema de banco de dados baseado no relacionamento
entre os objetos que se quer persistir. O segundo passo é criação dos objetos cujos estados
vão ser persistidos, isto é, a construção de classes (beans) para cada entidade do banco de
dados. Estas classes devem ser construídas seguindo o modelo JavaBeans, com métodos get
e set para manipulação dos atributos. Neste ponto, o Hibernate difere-se de outros
mecanismos de persistências como o JDO, visto que os beans utilizados pelo Hibernate não
necessitam estender superclasses ou implementar interfaces. É necessária a implementação
de um construtor default (construtor sem parâmetros) para todas as classes persistentes. O
Hibernate faz a instanciação de objetos e o acesso às suas propriedades através de reflexão,
assim métodos de acesso e construtores não necessitam ser declarados como públicos.
Adicionalmente, deve ser criada uma propriedade “chave-primária”, que fará às vezes de
uma primary key, através da qual um objeto será unicamente identificado. Esta propriedade,
não obrigatória, pode ser do tipo primitivo int, long, char ou mesmo uma String.
Após a criação das tabelas e dos beans é necessário criar meta-dados de modo a
fornecer ao Hibernate informações sobre como relacionar os objetos com as entidades do
banco de dados. Esta é a terceira etapa, a criação de arquivos XML que relacionam as
propriedades de cada objeto aos campos das tabelas. Nestes arquivos de mapeamento devese informar, ainda, qual propriedade do objeto se relaciona com a chave-primária, bem com
os relacionamentos entre entidades (inclusive os tipos de relacionamento: 1-1, 1-N ou NN).
A quarta etapa refere-se à criação de um arquivo contendo as propriedades
necessárias para que o Hibernate se conecte ao banco de dados. Existe um arquivo de
53
configuração modelo (hibernate.properties) que poderá ser utilizado como base para que o
usuário proceda à configuração. A quinta e última etapa é a criação de Data Access Objects
(DAO), Tais mecanismos são design pattern úteis para separação da lógica de acesso a
dados da lógica de negócios da aplicação. Estas classes é que conterão os métodos de
inclusão, alteração, exclusão dos objetos, etc.
Em resumo, o Hibernate é uma ferramenta que permite trabalhar com persistência
sobre banco de dados, sem necessidade da inclusão de instruções SQL em meio ao código
Java, assim como elimina a necessidade de se mapear ResultSets e implementar
configuração de pool de conexões, etc., o que torna o código mais legível e,
conseqüentemente, mais fácil de manter. Contudo a implementação não é independente da
fonte de dados, visto que o Hibernate trabalha apenas com alguns dos bancos de dados
relacionais. Por outro lado, caso a aplicação exija consultas SQL complexas, há de se
considerar a utilização da linguagem HQL, a Query Language do Hibernate.
Tabela 14 - Comparações do Hibernate com outros frameworks de persistência
54
4.5. OJB
O OJB (Object Relational Bridge) faz parte do projeto Jakarta, da fundação Apache,
que desenvolve ferramentas de código aberto para Java. Prestes a ter sua primeira versão
oficial lançada o OJB é baseado em um cerne de mapeamento objeto relacional a partir do
qual várias APIs podem ser disponibilizadas. O OJB e' muito flexivel na forma em que pode
ser usado. O desenvolvedor tem opções de 3 diferentes API:
Uma API compativel com o padrao ODMG 3.0
Uma API compativel com o padrão JDO da Sun (ainda incompleta).
A PersistenceBroker API que server como o núcleo das demais APIs usada no OJB. Essa API
pode ser usada por qualquer tipo de aplicação.
Podemos considerar OJB comparado ao Hibernate em termos de suporte aos conceitos OO
e funcionalidades. O OJB apresenta as vantagens de possuir melhor suporte a distribuição
(com sincronização de cachê) e ser aderente a padrões estabelecidos.
O mapeamento objeto-relacional do OJB é feito através de um único arquivo XML,
cujas marcações fazem parte de um DTD próprio, e para o mapeamento de hierarquias de
classe são permitidos todos os tipos de particionamento (tipado, vertical e horizontal).
Inicialmente foi tentado o mapeamento sobre a mesma base de dados utilizada nos testes do
Hibernate, com particionamento vertical. Contudo, testes preliminares detectaram algumas
falhas do OJB na execução deste tipo de mapeamento. Devido a este problema o programa
de carga utilizado para a geração da base de dados de testes do Hibernate foi modificado
para que também fossem geradas na base tabelas que suportassem o particionamento
horizontal das classes. A base de dados passou então a ser usada tanto pelo Hibernate como
pelo OJB, porém com mapeamentos sobre conjuntos diferentes de tabelas.
A implementação do modelo de classes, em comparação com o Hibernate, foi um
pouco mais complicada. No caso do OJB também foram utilizadas classes simples, apenas
com atributos protegidos e métodos de acesso e modificação.
Contudo, por uma restrição do OJB, para que fosse possível o uso de sua
funcionalidade de carga de objetos sob demanda foi necessário a definição de interfaces, e
fazer que as classes as implementassem, também, de forma semelhante ao caso do
Hibernate, não foi possível declarar algumas das classes como abstratas.
55
Cliente
CLIENT APPLICATION
OJB Layers
Backends
Componentes do OJB
● O JDO Suporta:
– Hypersonic SQL
– Lutris InstantDB
– IBM DB2
– Oracle
– MS Access
– MS SQL Server 2000
-Instalar/Configurar
56
Toda a funcionalidade da versao 1.0 ja esta presente na versao atual. Baixe a versao
binaria
do
OJB,
a
menos
que
você
queira
dar
uma
olhada
na
fonte.
O arquivo que esta disponível para download no web site tem valioas arquivos p/
configurar e criar os tutoriais, e algumas aplicações dentro do OJB. Vou descrever todos os
arquivos necessários para uma aplicação OJB rodar.
-Arquivos
Esses são os arquivos JAR que você precisa no classpath de uma aplicação OJB:
antlr.jar
commons-collections-2.0.jar
commons-dbcp.jar
commons-lang-1.0-mod.jar
commons-pool.jar
db-ojb-1.0.rc2.jar
jta-spec1_0_1.jar
O arquivo OJB.properties contem configurações especificas de como o ambiente OJB
deve rodar. Nele você configura que pool de conexões quer usar, qual a política de cachê a
ser usada, em que arquivo esta a configuração do banco de dados (chamada de repositório),
e varias outras opções. Em geral, não se precisa mudar nada nesse arquivo, mas, ter uma
boa noção e' importante p/ saber que partes do OJB você pode configurar.
Chegou a vez do arquivo mais importante, onde se configura a aplicação que esta
sendo desenvolvida, e o repository.xml, ele contem chamada a outros arquivos XML,
sendo:
-repository_database.xml, contem a configuração do acesso ao banco de dados. Aqui, você
especifica o banco, usuário, senha, enfim, as configurações normais de uma conexão JDBC.
Também se pode setar opções do pool de conexões, como a quantidade máxima e inicial de
conexões a serem abertas.
57
-repository_user.xml, contem o mapeamento das classes Java as tabelas no banco de dados.
Para cada classe, você deve criar uma definição como esta a seguir:
Code:
<class-descriptor
class="br.com.javafree.ojb.Produto"
table="jf_produto"
>
<field-descriptor id="1"
name="produtoId"
column="produto_id"
jdbc-type="INTEGER"
primarykey="true"
autoincrement="true"
/>
<field-descriptor id="2"
name="descricao"
column="produto_desc"
jdbc-type="VARCHAR"
/>
</class-descriptor>
Nesse exemplo, temos uma classe br.com.javafree.ojb.Produto que corresponde a tabela
jf_produto, os campos produto_id e producto_desc correspondem as propriedades id e
descrição na classe.
A fase de construção do repository_user.xml e' um pouco trabalhosa, mas,
os desenvolvedores do OJB esta criando uma aplicação para criar o XML quando você
indica a base de dados, o que agilizara muito esse processo.
Chegou a vez do código, vou mostrar treis exemplos de operação no banco de dados,
uma parte do código é sempre necessária no uso do OJB, que é a chamado ao
PersistenceBroker, classe responsável pela operações no banco de dados.
Os pacotes org.apache.ojb.broker e org.apache.ojb.broker.query tem que ser importados.
SELECIONAR TODOS OS REGISTROS E IMPRIMIR
Code:
PersistenceBroker broker = null;
// indicamos que classe queremos buscar no banco de dados
Query query = new QueryByCriteria(br.com.javafree.ojb.Produto.class,
null);
try {
broker = PersistenceBrokerFactory.defaultPersistenceBroker();
// pedimos ao broker p/ gerar uma colecao como os dados do BD
Collection allProducts = broker.getCollectionByQuery(query);
// simplesmente imprimos as propriedades dos objetos retornados
java.util.Iterator iter = allProducts.iterator();
while (iter.hasNext())
{
br.com.javafree.ojb.Produto produto =
(br.com.javafree.ojb.Produto)iter.next();
out.println(produto.getId() + " - " + produto.getDescricao()
+ "<br>");
}
} catch (Throwable t) {
58
t.printStackTrace();
}
INSERIR UM NOVO REGISTRO
Code:
// try to save to DB using OJB
br.com.javafree.ojb.Produto novoProduto = new
br.com.javafree.ojb.Produto();
novoProduto.setDescricao("Livro de Java ");
// ojb
PersistenceBroker broker = null;
try {
broker =
PersistenceBrokerFactory.defaultPersistenceBroker();
broker.store(novoProduto);
}
catch(Exception e) {
e.printStackTrace();
}
Para inserir um registro, e' mais simples ainda, basta chamar o método store() do
PersistenceBroker que o OJB se encarrega de armazenar os dados no BD de acordo com o
que definido no arquivo repository_user.xml. Percebam que, pelo fato de termos definido o
campo produto_id como auto incremento, o próprio OJB se encarrega em calcular essa
valor usando a configuração no arquivo OJB.properties. Esse recurso (auto-incremento)
exige o uso de uma tabela interna do OJB, que pode ser criada com o seguinte comando
SQL, no meu caso, p/ MySQL:
Code:
CREATE TABLE `ojb_hl_seq` (
`TABLENAME` varchar(175) NOT NULL default '',
`FIELDNAME` varchar(70) NOT NULL default '',
`MAX_KEY` int(11) default NULL,
`GRAB_SIZE` int(11) default NULL,
PRIMARY KEY (`TABLENAME`,`FIELDNAME`)
)
5. ESTUDO DE CASO
5.1. DIAGRAMA DE CLASSE “BIBLIOTECA MÚSICA”
Na tabela 15 estão as classe que vão ser utilizada no mapeamento das
ferramentas estudadas.
59
TABELA 15 Diagrama de Classe “Biblioteca Música”;
5.1.2. Modelo de Mapeamento Hibernate:
Definição da classe Artista;
Import java.uitl.Set;
Import java.util.HashSet;
Public class Artista{
private Long id;
private String nome;
private Set cds = new HashSet();
}
Mapeamento da classe Artista
<?xml version”1.0” encoding= “UTF-8”?> // cabeçalho do xml
<!DOCTYPE hibernate-mapping PUBLIC // cabeçalho do hibernate
“-//Hibernate/Hibernate Mapping DTD 2.0//EN”
“http://hibernate.sourceforge.net/hibernate.mapping-2.0.dtd”>
<hibernate-mapping>
<class name= “Artista” table=”artista”>
<id name= “id” column=“id” type=“long” unsaved-value= “null”>
<generator class=“native”/>
<id>
60
<property
Name= “nome”
Type= “java.lang.String”
Column= “nome”
Length= “255”
Not-null= “true”
/>
<set name= “cds”lazy= “true” inverse= “true”>
<key column= “artista_id”/>
<one-to-many class= “Cd”/>
</set>
</class>
</hibernate-mapping>
Definição da classe Cd;
Import java.uitl.Set;
Import java.util.HashSet;
Public class Cd{
private Long id;
private String titulo;
private Capa capa;
private Artista artista;
private Set cds = new HashSet();
}
Mapeamento da classe Cd;
<?xml version”1.0” encoding= “UTF-8”?> // cabeçalho do xml
<!DOCTYPE hibernate-mapping PUBLIC // cabeçalho do hibernate
“-//Hibernate/Hibernate Mapping DTD 2.0//EN”
“http://hibernate.sourceforge.net/hibernate.mapping-2.0.dtd”>
<hibernate-mapping>
<class name= “Cd” table=”cd”>
<id name= “id” column=“id” type=“long” unsaved-value= “null”>
<generator class=“native”/>
<id>
<property
Name= “titulo”
61
Type= “java.lang.String”
Column= “titulo”
Length= “255”
Not-null= “true”
/>
<set name= “musicas”lazy= “true” inverse= “true”>
<key column= “cd_id”/>
<one-to-many class= “Musica”/>
</set>
<many-to-one name=”artista” column= “artista_id” not-null= “true”>
<one-to-one name= “capa” class= “Capa”/>
</class>
</hibernate-mapping>
Definição da classe Capa;
Import java.uitl.Set;
Import java.util.HashSet;
Public class Capa{
private Long id;
private String imagem;
private Cd cd;
}
Mapeamento da classe Capa;
<?xml version”1.0” encoding= “UTF-8”?> // cabeçalho do xml
<!DOCTYPE hibernate-mapping PUBLIC // cabeçalho do hibernate
“-//Hibernate/Hibernate Mapping DTD 2.0//EN”
“http://hibernate.sourceforge.net/hibernate.mapping-2.0.dtd”>
<hibernate-mapping>
<class name= “Capa” table=”capa”>
<id name= “id” column=“id” type=“long” unsaved-value= “null”>
<generator class=“native”/>
<id>
<property
Name= “imagem”
Type= “java.lang.String”
62
Column= “imagem”
Length= “255”
Not-null= “true”
/>
</class>
</hibernate-mapping>
Definição da classe Musica;
Public class Musica{
private Long id;
private String titulo;
private Cd cd;
}
Mapeamento da classe Musica;
<?xml version”1.0” encoding= “UTF-8”?> // cabeçalho do xml
<!DOCTYPE hibernate-mapping PUBLIC // cabeçalho do hibernate
“-//Hibernate/Hibernate Mapping DTD 2.0//EN”
“http://hibernate.sourceforge.net/hibernate.mapping-2.0.dtd”>
<hibernate-mapping>
<class name= “Musica” table=”musica”>
<id name= “id” column=“id” type=“long” unsaved-value= “null”>
<generator class=“native”/>
<id>
<property
Name= “titulo”
Type= “java.lang.String”
Column= “titulo”
Length= “255”
Not-null= “true”
/>
<many-to-one name=”cd” column= “cd_id” not-null= “true”>
</class>
</hibernate-mapping>
63
5.2. Modelo de mapeamento OJB
Definição da classe Artista;
Import java.uitl.Set;
Import java.util.HashSet;
Public class Artista{
private Long id;
private String nome;
private Set cds = new HashSet();
}
Mapeamento da classe Artista
<class-descriptor
Class=”br.com.javafree.ojb.produto”
Table=”jf_produto”>
<class name= “Artista” table=”artista”>
<id name= “id” column=“id” type=“long” unsaved-value= “null”>
<generator class=“native”/>
<id>
<field-descriptorid=”1”
Name= “nome”
Type= “java.lang.String”
Column= “nome”
Length= “255”
Not-null= “true”
/>
<class-descriptor>
Definição da classe Cd;
Import java.uitl.Set;
Import java.util.HashSet;
Public class Cd{
private Long id;
private String titulo;
private Capa capa;
private Artista artista;
64
private Set cds = new HashSet();
}
Mapeamento da classe Cd;
<class-descriptor
Class=”br.com.javafree.ojb.produto”
Table=”jf_produto”
>
<field-descriptorid=”1”
<class name= “Cd” table=”cd”>
<id name= “id” column=“id” type=“long” unsaved-value= “null”>
<generator class=“native”/>
<id>
<property
Name= “titulo”
Type= “java.lang.String”
Column= “titulo”
Length= “255”
Not-null= “true”
/>
<set name= “musicas”lazy= “true” inverse= “true”>
<key column= “cd_id”/>
<one-to-many class= “Musica”/>
</set>
<many-to-one name=”artista” column= “artista_id” not-null= “true”>
<one-to-one name= “capa” class= “Capa”/>
/>
<class-descriptor>
Definição da classe Capa;
Import java.uitl.Set;
Import java.util.HashSet;
Public class Capa{
private Long id;
private String imagem;
private Cd cd;
}
65
Mapeamento da classe Capa;
<class-descriptor
Class=”br.com.javafree.ojb.produto”
Table=”jf_produto”
>
<field-descriptorid=”1”
<class name= “Capa” table=”capa”>
<id name= “id” column=“id” type=“long” unsaved-value= “null”>
<generator class=“native”/>
<id>
<property
Name= “imagem”
Type= “java.lang.String”
Column= “imagem”
Length= “255”
Not-null= “true”
/>
<class-descriptor>
Definição da classe Musica;
Public class Musica{
private Long id;
private String titulo;
private Cd cd;
}
Mapeamento da classe Musica;
<class-descriptor
Class=”br.com.javafree.ojb.produto”
Table=”jf_produto”
>
<field-descriptorid=”1”
<class name= “Musica” table=”musica”>
<id name= “id” column=“id” type=“long” unsaved-value= “null”>
<generator class=“native”/>
<id>
66
<property
Name= “titulo”
Type= “java.lang.String”
Column= “titulo”
Length= “255”
Not-null= “true”
/>
<class-descriptor>
5.3. Essas são as tabelas do SQL
CREATE TABLE Cd (
ID
INT NOT NULL,
TITULO
VARCHAR(30) NOT NULL,
CAPA
INT NOT NULL,
ARTISTA
INT NOT NULL
PRIMARY KEY (ID),
FOREIGN KEY (CAPA)
REFERENCES Capa(ID),
FOREIGN KEY (ARTISTA)
REFERENCES Artista(ID)
);
CREATE TABLE Capa (
ID
INT NOT NULL,
IMAGEM
VARCHAR(50),
CD
INT NOT NULL
PRIMARY KEY (ID),
FOREIGN KEY (CD)
REFERENCES Cd(ID)
);
CREATE TABLE Musica (
ID
INT NOT NULL,
TITULO
VARCHAR(30) NOT NULL,
CD
INT,
PRIMARY KEY (ID),
FOREIGN KEY (CD)
REFERENCES Cd(ID)
);
67
CREATE TABLE Artista (
ID
INT NOT NULL,
NOME
VARCHAR(30),
PRIMARY KEY (ID)
);
5.4. Assim seria a inserção nas tabelas uma a uma
INSERT INTO Artista (ID, NOME) VALUES (55, ‘João’);
INSERT INTO Musics (ID, TITULO, CD) VALUES (123654, ‘MTV AO VIVO’, 53651);
6. RESULTADOS OBTIDOS
Todos os testes foram executados em um mesmo ambiente. Para a execução de cada
consulta foi adotado o seguinte procedimento: (1) iniciar o SGBD; (2) iniciar o programa
68
de execução da consulta; (3) finalizar o SGBD; (4) executar a leitura de arquivos em disco,
não relacionados aos testes, para invalidação de cachês e buffers do sistema operacional.
Antes da execução dos testes de desempenho, porém, foi feito também um teste inicial para
que fosse comprovada a consistência de resultados entre os diversos sistemas testados. Os
conteúdos dos resultados das consultas foram tomados como referência e posteriormente
comparados com os consteúdos dos resultados das consultas executadas pelo Hibernate e o
OJB.
O Hibernate e o OJB apresentam funcionalidades semelhantes aos resumos,
chamadas de consultas escalares e consultas de relatório, respectivamente.
O Hibernate e o OJB também apresentam funcionalidades para distribuição, sendo o
Hibernate mais restritivo e o OJB tendo suporte direto a sincronização de cachê e controle
de concorrência distribuído.
6.1. Vantagens e desvantagens da utilização do mapeamento usando a
ferramenta Hibernate
Usando a ferramenta:
O Hibernate torna transparente o acesso ao banco de dados, sendo que ele faz tudo
sozinho, ou seja, ele salva tudo na memória e quando você termina ele salva tudo no
banco.
O código fica menor e mais eficiente pois o Hibernate otimiza o código.
Você usa o SQL mas as tabelas são montadas pelo Hibernate.
Com Hibernate você montaria os objetos mas a inserção ele pegaria cada atributo e
montaria os comandos inserts sozinho.
Não Usando a ferramenta:
O código seria bem maior.
Teria que inserir toda as classes usando o SQL sendo que eu teria que montar as
tabelas sendo que o Hibernate já te entrega pronto.
Todo o acesso ao banco seria manual.
69
6.2. Vantagens e desvantagens da utilização do mapeamento usando a
ferramenta OJB
Usando a ferramenta:
Fácil de utilizar
O código fica bem menor
Ele inseri no banco de dados altomâticamente
Não Usando a ferramenta:
Não tem a característica de um banco de dados robusto
Por exemplo: indexs, transaccion...
Não é escalavel
O código ficaria bem maior
A inserção ao banco de dados seria manual
7. Conclusão
70
Apesar da relutância de alguns em adotar esquemas de persistência, fica evidente
que sua utilização trás um ganho considerável de tempo na implementação de um sistema e
eleva a qualidade do produto final, à medida que diminui a possibilidade de erros de
codificação. O fraco acoplamento entre as camadas de dados e de lógica do sistema
promovido pelas Camadas de Persistência é outro ponto que demonstra a sua utilidade.
Além de fornecer um acesso mais natural aos dados, as Camadas de Persistência executam
controle transacional, otimização de consultas e transformações automáticas de dados entre
formatos distinto (tabelas relacionais para arquivos XML ou classes Java, por exemplo).
Sem dúvida, as Camadas de Persistência devem funcionar como a principal ponte de
ligação entre sistemas Orientados a Objetos e repositórios de dados diversos: um conceito
poderoso, com implementações estáveis e comprovadamente eficientes.
71
8. REFERÊNCIA BIBLIOGRÁFICA
AGILE
DATA.
“Object-Relational
Mapping
–
An
Essay”.
[Internet:
http://www.agiledata.org/essays/mappingObjects.html, recuperado em 20/01/2005].
AMBYSOFT. [Internet:http://www.ambysoft.com/mappingObjects.html, recuperado em
20/01/2005].
DATE, C. J. “Introdução a Sistemas de Bancos de Dados”. Rio de Janeiro: Campus, 1990.
HIBERNATE. [Internet: http://www.hibernate.org, recuperado em 16/02/2005].
NASSU, E. A.; SETZER, W. W. “Banco de Dados Orientado a Objeto”. Editora Edgar
Bkucher Ltda., 1º edição 1999.
SILBERSCHATZ, A.; KORTH, H. F., SUDARSHAN, S. “Sistema de Banco de Dados”.
São Paulo: Makron Books, 1999.
MUNDO-OO.“Mapeando Objetos para Bancos de Dados Relacionais: técnicas e
implementações.”[Internet:http://www.mundooo.com.br/php/mooartigos.php?pa=show
page&pid=19, recuperado em 15/03/2005].
ULBRA“Persistência
de
Dados”[Internet:http://www.ulbra.tche.br/~roland/tcc-
gr/monografias/2003-2-tc2-Anelise_Rosa_Rodrigues.pdf, recuperado em 08/04/2005].
INTERSYSTEMS.“Problemas de Impedância”[Internet:http://www.intersystems.com.br
/isc/downloads/cachedoctec/ProblemadeImpedância.pdf, recuperado em 08/04/2005].
JAKARTA PROJECT. “Projeto Jakarta” [Internet: http://www.jakarta.apache.org
recuperado em 10/10/2005].
72
Download

Mapeamento Objeto