Posts Tagged ‘base de dados’

Mapeamento Objeto-Relacional (ORM)

1 \01UTC julho, 2008

Com o lançamento do PHP5, as esperanças de utilizar as técnicas da programação orientada à objetos (POO) no desenvolvimento de sistemas web cresceu para os adeptos da linguagem . A versão anterior do PHP já possuia recursos de orientação à objetos, mas estes eram muito limitados e alguns problemas chegavam a desencorajar seu uso. O PHP5 veio com um novo motor, o Zend Engine 2, que corrigiu esses problemas e suporta a maioria dos recursos esperados de uma linguagem orientada à objetos, como interfaces, métodos abstratos, herança, polimorfismo, dentre outros.

Apesar das linguagens modernas possuirem recursos da POO, os sistemas de gerenciamento de bases de dados (SGBD) são quase todos baseadas no modelo relacional, onde temos tabelas ao invés de objetos. E na maioria dos casos o que queremos é justamente guardar nossos objetos na base dados. Guardar instâncias de objetos em registros de tabelas é o objetivo das técnicas de mapeamento objeto-relacional (em inglês object-relational mapping, ORM).

Existem muitas diferenças entre os modelos relacional e orientado à objeto e a mais incômoda delas para quem desenvolve sistemas web é que tabelas não suportam herança.

Considere o seguinte exemplo: temos três classes, sendo uma delas abstrata e as outras duas concretas e extendendo a primeira. Mais palpável ainda: considere uma classe abstrata Pessoa e as classes concretas Fornecedor e Cliente que são filhas da classe Pessoa.

Como guardar essas informações no banco de dados?

Uma tabela por classe contreta

A solução mais imediata seria criar 2 tabelas, da seguinte forma:

Essa solução mais simples ignora completamente a hierarquia de classes e faz uma tabela por classe concreta sem relacionamentos.

  • Prós:
    • Todos os dados de um objeto estão em uma só tabela, facilitando a manipulação.
    • Buscas serão executadas de forma rápida.
    • Não há campos nulos.
  • Contras
    • Dificulta alterações na classe pai, pois os campos estão duplicados (ou triplicados, quadruplicados…).
    • Pode ser um grande problema se houver alguma classe com referência para a classe pai (veja próximo exemplo).

Em alguns casos talvez essa seja uma boa solução. Não é o caso se houver uma referência para a classe pai, como por exemplo:

UML - Referência à classe pai

Nesse caso a nossa primeira solução falha, pois não existe uma forma de relacionar Mensagem com Pessoa sem explicitar se é um Fornecedor ou um Cliente. Poderíamos claro, usar a chave estrangeira de todas as subclasses, mas isso seria um pesadelo no caso de muitos relacionamentos e/ou muitas subclasses. Precisamos de uma solução melhor.

Uma tabela por árvore de classes

Podemos utilizar 1 só tabela para todas as subclasses de Pessoa, solucionando o problema anterior:

Nesta solução temos 1 tabela para cada árvore de classes. O campo tipo irá determinar se o registro é um Cliente ou Fornecedor. O campo endereço ficará nulo no caso dos clientes e o campo telefone ficará nulo no caso dos fornecedores.

  • Prós:
    • Todas as informações de uma instância estão na mesma tabela, o que facilita a manipulação.
    • Permite referências à classe pai.
  • Contras:
    • Muitos campos nulos – pode ser muito deselegante para grandes árvores de classes.
    • Muitos registros na mesma tabela podem retardar as busca. Mesmo que indexemos o campo tipo teremos problemas, já que só é utilizado um índice por busca.
    • Registros muito grandes podem retardar as buscas.
    • Campo tipo extra.
    • Não permite referência específica às subclasses.

Note que essa solução falha se precisarmos ter uma referência para a subclasse. Se as mensagens, por exemplo, pudessem ser trocadas apenas entre clientes, não haveria como fazer essa restrição no banco de dados. E se nossa árvore de classes for grande a tabela se tornará um monstro com dezenas de campos nulos em cada registro. Precisamos ainda de uma solução melhor.

Uma tabela por classe

Podemos mapear cada classe para uma tabela, fazendo a representação da hereança por um relacionamento 1 para n:

Assim temos 1 tabela por classe, relacionando as classes de forma a mater a árvore e usando o campo tipo para indicar a subclasse. Na hora de buscar um registro, checa-se esse campo para saber qual é a classe e, logo, qual outra tabela consultar.

  • Prós:
    • Mantém as hierarquia das classes.
    • Não contém campos nulos.
    • Não quebra o encapsulamento de implementação da classe pai.
    • Permite fazer referências tanto a classe pai quanto às subclasses.
    • Registros pequenos, com poucos campos.
  • Contras:
    • Os atributos de um objeto estão espalhados em mais de uma tabela, tornando as buscas mais complexas.
    • As buscas podem ficar mais lentas pelo uso excessivo de joins.
    • Campo tipo extra.

Essa solução é mais completa, podendo ser utilizada em todas as situações. Além disso ela tem uma boa relação custo/benefício até para situações onde as outras soluções também são possíveis.

Conclusão

Recomendo utilizar a solução de uma tabela por classe sempre. O motivo é simples: mesmo que as outras soluções sejam aplicáveis em algumas situações, essas situações podem mudar na próxima alteração, forçando a implementar uma tabela por classe . Além disso, a perda de desempenho pode ser bastante minimizada pela utilização correta de índices.