UNIVERSIDADE PRESBITERIANA MACKENZIE CARLOS ALBERTO LINS GONÇALVES DOS SANTOS LUIZ EDUARDO DE FRANCO MACEDO MARCELO ROSIN CITRANGULO MARCO AURELIO GIOVANI VISCONTI PREVALÊNCIA – PERSISTÊNCIA TRANSPARENTE DE OBJETOS EM MEMÓRIA São Paulo 2003 CARLOS ALBERTO LINS GONÇALVES DOS SANTOS LUIZ EDUARDO DE FRANCO MACEDO MARCELO ROSIN CITRANGULO MARCO AURELIO GIOVANI VISCONTI PREVALÊNCIA – PERSISTÊNCIA TRANSPARENTE DE OBJETOS EM MEMÓRIA Trabalho apresentado à disciplina de Trabalho de Graduação Interdisciplinar II, como parte das exigências para a obtenção do título de Bacharel em Sistemas de Informação pela Faculdade de Computação e Informática da Universidade Presbiteriana Mackenzie ORIENTADOR: ROGÉRIO DE OLIVEIRA São Paulo 2003 SUMÁRIO RESUMO ............................................................................................ ABSTRACT .......................................................................................... 1 – INTRODUÇÃO ..................................................................................... 2 – CONCEITOS ....................................................................................... 2.1 – SISTEMA GERENCIADOR DE BANCO DE DADOS ........................................ 12 2.1.1 – Sistemas Gerenciadores de Bancos de Dados Relacionais............................... 13 2.1.2 – Sistemas Gerenciadores de Bancos de Dados Orientados a Objeto ................. 16 2.2 – CARACTERÍSTICAS DE UM SGBD ................................................................... 18 2.2.1 – Gerenciamento de Transações .......................................................................... 18 2.2.2 – Serialização....................................................................................................... 21 2.2.3 – Controle de Concorrência................................................................................. 22 2.2.4 – Recuperação...................................................................................................... 22 2.2.5 – Log de Transação.............................................................................................. 22 2.3 – CARACTERÍSTICAS DE UM SGBDOO.............................................................. 24 2.3.1 – Objetos Complexos .......................................................................................... 24 2.3.2 – Identificador de Objetos ................................................................................... 25 2.3.3 – Encapsulamento................................................................................................ 26 2.3.4 – Tipos ou classes ................................................................................................ 27 2.3.5 – Herança............................................................................................................. 28 2.3.6 – Acoplamento tardio (late binding).................................................................... 29 2.3.7 – Completeza Computacional.............................................................................. 30 2.3.8. – Extensibilidade ................................................................................................ 31 2.3.9 – Persistência de Objetos..................................................................................... 32 2.3.10 – Capacidade de gerenciamento de armazenamento secundário....................... 33 2.3.11 – Capacidade de prover concorrência................................................................ 33 2.3.12 – Recuperação.................................................................................................... 33 2.3.13 – Consulta simples de dados (queries “ad hoc”) ............................................... 33 2.4 – PERSISTÊNCIA ..................................................................................................... 34 2.4.1 – Persistência Ortogonal...................................................................................... 35 2.5 – LINGUAGENS DE PROGRAMAÇÃO PERSISTENTES .................................... 36 2.6 – INCOMPATIBILIDADE DE IMPEDÂNCIAS (IMPEDANCE MISMATCH).... 37 3 – SISTEMAS PREVALENTES .......................................................................... 3.1 – BREVE HISTÓRICO.............................................................................................. 39 3.2 – DEFINIÇÕES DE PREVALÊNCIA....................................................................... 39 3.3 – ESQUEMA GERAL DE FUNCIONAMENTO DA PREVALÊNCIA.................. 42 3.4 – IMPLEMENTAÇÕES: PREVAYLER E BAMBOO.PREVALENCE .................. 49 3.5 – VANTAGENS E DESVANTAGENS DA PREVALÊNCIA................................. 52 3.5.1 – Eliminação da tecnologia relacional................................................................. 52 3.5.2 – Ambiente unificado .......................................................................................... 54 3.5.3 – Representação única da solução ....................................................................... 55 3.5.4 – Armazenamento em memória........................................................................... 57 3.6 – APLICAÇÕES POTENCIAIS DE SISTEMAS PREVALENTES......................... 57 4 – IMPLEMENTAÇÃO ................................................................................. 4.1 – PRÉ-REQUISITOS ................................................................................................. 59 4.2 – DESENHO DA APLICAÇÃO................................................................................ 60 4.3 – AMBIENTE WEB................................................................................................... 62 4.4 – ADICIONANDO A PREVALENCIA AO MODELO ........................................... 63 4.5 – IMPLEMENTANDO A PREVALÊNCIA.............................................................. 65 4.6 – MANIPULANDO OBJETOS PERSISTENTES .................................................... 68 4.6.1 – Inclusão............................................................................................................. 69 4.6.2 – Consulta............................................................................................................ 72 4.6.3 – Alteração........................................................................................................... 74 4.6.4 – Exclusão ........................................................................................................... 77 4.7 – DESEMPENHO ...................................................................................................... 79 5 – CONCLUSÃO ...................................................................................... 6 – REFERÊNCIAS BIBLIOGRÁFICAS ................................................................ 7 – BIBLIOGRAFIA COMPLEMENTAR ................................................................ INDÍCE DE FIGURAS Figura 1 - Modelo Entidade Relacionamento de um sistema de Banco de Dados............... 14 Figura 2 - Sistema Gerenciador de Banco de Dados Orientado a Objeto. ........................... 17 Figura 3 - Serialização da execução de duas transações. ..................................................... 21 Figura 4 - Representação gráfica do conceito de encapsulamento....................................... 27 Figura 5 – Diagrama UML representando herança de classes. ............................................ 29 Figura 6 - Relação de inventário com tipo de dado pré-determinado. ................................. 30 Figura 7 - Classe inventário com acoplamento prévio. ........................................................ 30 Figura 8 - Classe inventário com acoplamento tardio. ......................................................... 30 Figura 9 - Serialização do estado de um objeto.................................................................... 43 Figura 10 - Diagrama temporal do início de execução de um sistema prevalente. .............. 45 Figura 11 - Diagrama temporal de um sistema prevalente em funcionamento. ................... 48 Figura 12 – Arquivo XML que representa a alteração da estrutura de um objeto. .............. 50 Figura 13 - Três mapeamentos com duas representações da solução................................... 55 Figura 14 - Um único mapeamento representando a solução............................................... 56 Figura 15 - Modelo de objetos.............................................................................................. 61 Figura 16 - Ambiente de uma aplicação web. ...................................................................... 63 Figura 17 - Modelo de objetos com suporte à prevalência................................................... 64 Figura 18 - Objetos persistentes e transientes. ..................................................................... 67 Figura 19 - Diagrama de sequência da inclusão de um objeto persistente. .......................... 71 Figura 20 - Diagrama de sequência da consulta de um objeto persistente ........................... 74 Figura 21 - Diagrama de sequência de alteração de um objeto persistente.......................... 77 Figura 22 - Diagrama de sequência de exclusão de um objeto persistente. ......................... 79 INDÍCE DE TABELAS Tabela 1 – Bancos de Dados Orientados a Objeto e de seus respectivos fornecedores. ...... 17 Tabela 2 - Características Mandatórias e Opcionais de um SGBDOO. ............................... 24 Tabela 3 – Tempos de duração dos valores de dados........................................................... 34 Tabela 4 – Implementações da prevalência em diversas linguagens.................................... 51 Tabela 5 - Resultados dos testes de desempenho. ................................................................ 80 INDÍCE DE EXEMPLOS DE CÓDIGO Exemplo 1 - Serialização automática com o uso do atributo [Serializable]......................... 65 Exemplo 2 - A função Application_Start ............................................................................. 66 Exemplo 3 - Criação do objeto engine. ................................................................................ 66 Exemplo 4 - Criação do objeto raiz da persistência. ............................................................ 66 Exemplo 5 - Adicionando objetos ao contexto da aplicação................................................ 67 Exemplo 6 - Exemplo de uma classe de comando. .............................................................. 68 Exemplo 7 - Exemplo de uma classe de consulta................................................................. 69 Exemplo 8 - Exemplo de uma classe de inclusão................................................................. 69 Exemplo 9 - Código que invoca o comando que torna o objeto persistente. ....................... 70 Exemplo 10 - Código que cria uma classe de consulta. ....................................................... 73 Exemplo 11 - Código que invoca uma classe de consulta.................................................... 73 Exemplo 12 - Código da classe de alteração do objeto Course. .......................................... 75 Exemplo 13 - Código de alteração da classe Course. .......................................................... 75 Exemplo 14 - Código da classe de comando que remove o objeto do tipo Course. ............ 78 Exemplo 15 - Código cliente que chama o comando que remove o objeto do tipo Course.78 RESUMO O objetivo deste estudo é analisar e demonstrar a utilização da prevalência no desenvolvimento de sistemas orientados a objeto como alternativa para a persistência de dados. A prevalência é conceituada e seu uso é comparado aos principais sistemas que fornecem o serviço de persistência de dados, como os bancos de dados relacionais e orientados a objeto. Um esquema geral do funcionamento da prevalência é abordado a fim de fornecer um melhor entendimento desse modelo de persistência de objetos. As vantagens e desvantagens, assim como os requisitos que melhor são atendidos quando da utilização da prevalência, como camada de persistência de objetos, são também descritos. Na parte prática do estudo, implementou-se um sistema orientado a objeto utilizando a prevalência para persistir objetos. A partir desse sistema, executaram-se testes para avaliar e discutir questões como o espaço de armazenamento e o desempenho da busca de objetos. Todas as informações apresentadas sobre a prevalência neste estudo têm como propósito auxiliar o leitor a verificar se este modelo é a escolha mais adequada para o fornecimento do serviço de persistência no desenvolvimento de sistemas orientados a objeto. ABSTRACT The goal of this research is analyzing and demonstrating the using of the prevalence at the development of object-oriented systems as an alternative for data persistence. The concepts concerning the prevalence are shown. The using of prevalence is compared to the main systems providing the service of data persistence, such as relational and objectoriented databases. A general schema of prevalence´s working is approached in order to provide a better understanding about this object persistence model. There is a description about all advantages and disadvantages using the prevalence, as well the requirements met at the using of prevalence, such as the object persistence layer. At the practical part of this research, it was implemented an object-oriented system by using the prevalence to persist objects. It were performed tests based on this system in order to evaluate and to discuss issues, such as storage space and the performance of object search. All information provided at this research about prevalence are driven to help the reader to verify that this model is the best choice for providing a persistence service at the development of object-oriented systems. 1 – INTRODUÇÃO A persistência é uma característica fundamental para o armazenamento de objetos, permitindo que perdurem para serem recuperados posteriormente. Na maioria dos casos, a persistência é alcançada através de sistemas gerenciadores de banco de dados relacionais que, para proverem o serviço de persistência, requerem adequações do modelo relacional e do sistema orientado a objeto. Alternativas de modelos de persistência têm sido estudadas para tentar superar os problemas causados por essas adaptações, dentre elas pode-se citar os sistemas gerenciadores de banco de dados orientados a objeto e as linguagens de programação persistentes. Um modelo emergente de adicionar persistência aos sistemas orientados a objeto é a prevalência. A prevalência é um modelo transparente de persistência de objetos baseado no armazenamento dos objetos na memória principal do computador, característica que a diferencia dos sistemas convencionais. Este trabalho encontra-se organizado como segue. O Capítulo 2 abrange conceitos relacionados à persistência e sistemas que fornecem esse serviço, como gerenciadores de banco de dados relacionais e orientados a objeto, e suas principais características. Os sistemas gerenciadores de banco de dados orientados a objeto são tratados com maior detalhe, pois conceitualmente trazem maiores semelhanças com os sistemas prevalentes do que os relacionais, sobretudo pelo uso de objetos. Finalmente, a incompatibilidade de impedâncias é descrita para ilustrar o problema da utilização de bancos de dados relacionais como camada de persistência em sistemas orientados a objeto. O Capítulo 3 trata do conceito de prevalência. O funcionamento desse sistema, as principais implementações e as aplicações. Ressaltam-se as vantagens e as desvantagens dos 10 sistemas prevalentes quando comparados com os sistemas gerenciadores convencionais (relacional e orientado a objeto). No Capítulo 4, exemplifica-se um sistema prevalente através de uma implementação baseada em código aberto. 11 2 – CONCEITOS 2.1 – SISTEMA GERENCIADOR DE BANCO DE DADOS Um banco de dados, ou um sistema de banco de dados, pode ser definido como um depósito contendo um conjunto de arquivos de dados, objetivando, o armazenamento, a disponibilização de informações e a manutenção. Envolve quatro componentes principais: hardware, software, dados e usuários [Date, 1991]. Adicionando ao conjunto de arquivos, uma série de programas que fornecem um ambiente conveniente e eficiente para recuperar e armazenar informações, tem-se um Sistema Gerenciador de Banco de Dados (SGBD) [Silberschatz, 1999]. O SGBD provê segurança para os dados armazenados e são projetados para o gerenciamento de grandes volumes de informação de natureza persistente, permitindo operações eficientes sobre esses dados. Os SGBDs mostram-se superiores em comparação aos sistemas de arquivos, fornecendo maior facilidade de acesso aos dados, melhor segurança, integridade, minimização da redundância e acessos concorrentes mais estáveis. Apresentam ainda as propriedades essenciais de atomicidade, consistência, isolamento e durabilidade (propriedades ACID). Os SGBDs buscam fornecer maior transparência no acesso aos dados. Segundo [Silberschatz, 1999], a proposta maior é prover aos usuários uma visão abstrata dos dados, isto é, o sistema omite certos detalhes de como os dados são armazenados e mantidos. Desta forma, a complexidade está escondida dos usuários através de diversos níveis de abstração que simplificam a interação do usuário com o sistema. 12 Um gerenciador pode administrar mais de um banco de dados, por exemplo, uma faculdade pode possuir um banco de dados para o controle de seus alunos e um outro banco de dados para os livros de sua biblioteca e diferentes usuários podem acessar tais bancos através do mesmo SGBD. 2.1.1 – Sistemas Gerenciadores de Bancos de Dados Relacionais Apesar da história relatar outros modelos de banco de dados (o modelo de rede e também o modelo hierárquico), o modelo relacional foi um dos primeiros a serem utilizados com grande sucesso nas aplicações comerciais. O nome desse modelo de dados vem dos conceitos matemáticos de relações e de conjuntos. Nesse modelo, as informações que identificam uma determinada entidade (ou objeto) do mundo real são armazenadas em um formato tabular (relações), onde a tabela é o produto da intersecção entre linhas (ou tuplas) e colunas. Pode-se representar uma entidade do mundo real em uma tabela ou em diversas tabelas. Uma linha é a representação de uma entidade e a tabela é composta por um conjunto de entidades. Considere-se uma entidade “aluno”, com os atributos Nome, Endereço, CPF, RG e Matrícula. Esses atributos são as colunas da tabela “aluno” e cada um dos alunos de uma determinada entidade de ensino são as linhas desta mesma tabela. Cada dado deve ser identificado e transformado em atributos, os quais formam as colunas dessa tabela, enquanto cada linha representa uma entidade, identificando os diversos objetos desse tipo que fazem parte do sistema [Silberschatz, 1999] [Rob e Coronel, 1995]. Existem vários métodos e técnicas para modelar os objetos do mundo real e representá-los em tabelas, para que os mesmos possam ser armazenados em um sistema de 13 banco de dados relacional, que só “entende” relações. Neste trabalho, entretanto, é suficiente considerar apenas os conceitos já descritos. Figura 1 - Modelo Entidade Relacionamento de um sistema de Banco de Dados. As interações que são realizadas com um banco de dados dependem do sistema e da necessidade da aplicação. É necessário ainda, que exista, uma linguagem de consulta para as diferentes possibilidades de interação desejadas. Dependendo da necessidade, a manipulação dos dados pode ser implementada em uma linguagem de consulta do tipo procedural ou não-procedural ou, até mesmo, utilizando os dois tipos de linguagens. Apesar de existirem outras linguagens de consulta, a mais usual nos bancos de dados relacionais é a Structured Query Language (SQL). É uma linguagem padronizada para acesso a dados, permitindo, além da consulta ao banco de dados, a definição das estruturas e 14 manipulação dos dados, assim como, a especificação de restrições de segurança para acesso ao banco de dados. Com a evolução tecnológica, a difusão das redes e a utilização do computador em diversas áreas, surgiram muitos tipos de dados. Novas aplicações foram desenvolvidas com características diversas, como exemplo, as aplicações multimídia com dados do tipo áudio, imagem, vídeo etc., que demandam o armazenamento e a manipulação desses tipos de dados e outros mais complexos. Os bancos de dados relacionais não são tão capazes de lidar com os tipos de dados que essas novas aplicações exigem [Silberschatz, 1999] e surgem, então, novas alternativas. 15 2.1.2 – Sistemas Gerenciadores de Bancos de Dados Orientados a Objeto Devido às limitações do modelo relacional de dados no tratamento das novas aplicações, vários modelos de dados foram e estão sendo propostos. Entre eles, o modelo orientado a objeto [Silberschatz, 1999]. Na busca de dar suporte a novas tecnologias e aplicações integradas abrangendo dados multimídia (imagens, gráficos, dados de voz e vídeo), objetos espaciais com estrutura gráfica, dados de experimentos científicos, de telecomunicações, de sistemas de informação geográfica etc., surgiu o Banco de Dados Orientado a Objeto. Um Banco de Dados Orientado a Objeto (BDOO) busca armazenar dados de natureza complexa, como os dados de aplicações de Engenharia de Software Auxiliada por Computador (CASE), de Desenho Auxiliado por Computador (CAD), de Manufatura Auxiliada por Computador (CAM) e de Sistemas de Informação de Escritório (OIS). Os Bancos de Dados Orientados a Objeto apenas se tornaram realidade a partir do desenvolvimento das linguagens de programação orientadas a objeto. Na programação orientada a objeto os programas podem conter, além dos tipos de dados tradicionais (string, inteiro, float etc), as chamadas classes, que são uma extensão dos tipos de dados tradicionais e, também, podem conter procedimentos (métodos) que definem o quê e como podem ser realizadas as operações com esses objetos (comportamento dos objetos). Os dados dos programas criados com a linguagem orientada a objeto são armazenados em objetos durante a execução do programa [Nassu e Setzer, 1999]. 16 Os Sistemas Gerenciadores de Bancos de Dados Orientados a Objeto (SGBDOOs) utilizam as características da linguagem de programação orientada a objeto, em conjunto com as características de um sistema de banco de dados convencional [Khoshafian, 1994]. Figura 2 - Sistema Gerenciador de Banco de Dados Orientado a Objeto. Comercialmente, começam a surgir por volta dos anos 80. A tabela 1 traz alguns exemplos de protótipos e produtos comerciais de SGBDOO e seus respectivos fornecedores. Banco de Dados Orientado a Objeto ORION OPENOOBB IRIS ODE ENCORE/OB OBJECTIVITY JASMINE Empresa/universidade Microelectronics and Computer Technology Corporation Texas Instruments Hewlett Packard laboratories AT & T Bell Labs Server Brown University Objectivity Inc Computer Associates (CA) Tabela 1 – Bancos de Dados Orientados a Objeto e de seus respectivos fornecedores. Os BDOOs integram duas tecnologias: a de banco de dados e a de orientação a objeto. Portanto, trazem a combinação dos benefícios dos conceitos da orientação a objeto com a funcionalidade dos bancos de dados [Khoshafian, 1994]. 17 2.2 – CARACTERÍSTICAS DE UM SGBD A seguir são abordadas algumas das principais características dos SGBDs, como gerenciamento de transações, serialização, controle de concorrência, recuperação e log de transação. 2.2.1 – Gerenciamento de Transações A realização de atividades dentro de uma aplicação é executada por operações que, normalmente, constituem uma unidade lógica de trabalho. “A transação desempenha uma função lógica e é composta por uma coleção de operações” [Silberschatz, 1999]. Transações representam eventos do mundo real, como por exemplo a venda de produtos ou o depósitos de valores em conta. Executar e gerenciar transações são importantes atividades dos sistemas gerenciadores de banco de dados. Uma transação pode consistir de uma simples busca para gerar uma lista de conteúdos de tabela ou pode consistir de uma série de comandos seqüenciais. Por exemplo, para realizar uma atividade de transferência de fundos bancários (uma única unidade lógica de trabalho – uma transação), várias operações são executadas. Para citar algumas: • Quando o valor sai de uma determinada conta, uma operação de subtração deve ser realizada, assim como a atualização desse saldo; • Quando o valor entra na conta de destino, uma operação de soma deve ser realizada, assim como a atualização de saldo desta conta também. 18 Esse exemplo simples demonstra que a atividade realizada por uma transação, pode ser composta por diversas operações diferentes. As principais propriedades que um SGBD deve prover a uma transação são conhecidas como propriedades ACID e são: • Atomicidade; • Consistência; • Isolamento; • Durabilidade. O conceito de Atomicidade (da propriedade ACID) refere-se ao fato da necessidade da transação ser atômica, ou seja, não se pode dividir as operações que a compõe, ou todas as operações que compõe a transação são efetuadas por completo ou nenhuma será. A Consistência é outra propriedade (ACID) importante. Se o banco de dados está inicialmente consistente, a execução de uma transação deve levar o banco de dados a outro estado também consistente. As diferentes operações que compõem uma transação podem ser executadas por programas e/ou rotinas diferentes. Esses programas executados isoladamente levam a uma inconsistência dos dados em um determinado instante, obrigando o sistema de banco de dados a manter a consistência dos dados após a execução de todos os programas que compõem a transação. Usando o mesmo exemplo anterior de transferência de fundos entre contas, esse conceito ficará mais claro. Na transação de transferência executada, dois programas distintos, 19 um para débito em conta e outro para crédito em conta são executados isoladamente um após o outro. Após a execução do programa de débito e antes da execução do programa de crédito, o banco de dados estará em um estado de inconsistência, pois neste instante é como se o fundo da conta de origem tivesse desaparecido sem motivo. Após a execução do programa de crédito na conta de destino o banco de dados deverá voltar ao estado de consistência anterior a execução da transação. O resultado final coincide com o inicial, ou seja, o resultado da soma dos saldos da conta origem e destino deverá ser o mesmo antes e depois da transação, pois o valor que saiu de uma conta entrou na outra. No instante em que a transação de transferência de fundos entre contas estiver em execução, diversas operações serão realizadas com os dados das contas e nenhuma outra transação poderá alterar os dados dessa(s) conta(s) antes da transação de transferência terminar, ou seja, uma transação deve ser executada isoladamente (separadamente) de outra que acessa os mesmos dados. Os bancos de dados também devem ter mecanismos para manter a consistência dos dados, mesmo no caso em que falhas de software ou hardware ocorram, e devem também, permitir a execução de transações de forma concorrente. “A execução simultânea de transações proporciona uma melhoria de desempenho significativa” [Silberschatz, 1999]. Neste aspecto, o conceito de Isolamento (da propriedade ACID) torna-se muito importante em um sistema de banco de dados, no intuito de não permitir que os dados possam ficar inconsistentes devido às transações concorrentes, havendo a necessidade de mecanismos de isolamento de transações adequados. 20 A propriedade que garante a persistência dos dados, é a Durabilidade (ACID). Após a execução com sucesso de uma transação, os dados devem ser persistidos, ou seja, gravados em um meio físico de armazenamento não volátil, pois caso haja uma falha de energia, por exemplo, os dados poderão ser recuperados para utilização posterior. 2.2.2 – Serialização A fim de conseguir um maior nível de isolamento, muitos sistemas de banco de dados implementam a chamada serialização, onde as transações concorrentes são tratadas como se elas fossem executadas em ordem serial (uma após a outra), conforme demonstrado na figura 3. Desta forma, somente uma única transação está ativa de cada vez. Essa propriedade é importante, tanto em banco de dados multiusuários como distribuídos, onde muitas transações são executadas concorrentemente. Figura 3 - Serialização da execução de duas transações. 21 2.2.3 – Controle de Concorrência O SGBD deve controlar a interação entre as transações concorrentes a fim de não prejudicar a consistência. Tal controle é feito através de uma diversidade de mecanismos: os esquemas de controle de concorrência. Vários esquemas de controle de concorrência agem atrasando uma operação ou abortando a transação, buscando assegurar a serialização. Para tanto, algumas técnicas para controle são utilizadas, tais como: protocolos de bloqueio (lock); ordenação por registro de tempo (timestamp), técnicas de validação, granularidade múltipla e multiversão [Silberschatz, 1999]. 2.2.4 – Recuperação As possíveis falhas que podem ocorrer em um banco de dados (falhas de transação, de sistema ou meio) podem gerar um estado inconsistente. Desta forma, cabe a um sistema de banco de dados possuir um esquema de recuperação para detectar tais falhas e, caso ocorram, recuperar as informações pertinentes, retornando o banco de dados a um estado consistente, anterior à falha. O SGBD deve garantir, através dos esquemas de recuperação, que as propriedades de atomicidade e durabilidade sejam preservadas, mesmo com a possibilidade de falhas [Silberschatz, 1999]. Uma das formas usuais de se realizar a recuperação é através do mecanismo de log de transação. 2.2.5 – Log de Transação Um log de transação mantém as informações de todas as transações que atualizaram os dados em um sistema de banco de dados. Tais informações são utilizadas pelo sistema quando 22 ocorrer uma falha do próprio sistema, seja hardware ou software, términos anormais etc. A restauração de um sistema de banco de dados corrompido torna-se facilitada através do uso do log de transação. O log de transação armazena os dados que participam da transação antes e depois da mesma ser executada, além de algumas das tabelas, tuplas, e valores de atributos. O começo e o fim (COMMIT) da transação também são gravados [Rob e Coronel, 1995]. A fim de que os registros de log sejam úteis na recuperação após falhas de sistema e disco, o log deve residir em armazenamento estável (disco rígido) [Silberschatz, 1999]. 23 2.3 – CARACTERÍSTICAS DE UM SGBDOO Um Sistema Gerenciador de Banco de Dados Orientado a Objeto (SGBDOO) contém características da orientação a objeto e de um sistema gerenciador de banco de dados. As características mandatórias e opcionais são demonstradas na tabela 2 [Atkinson, 1989]: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 1. 2. 3. I. CARACTERÍSTICAS MANDATÓRIAS DE UM SGBDOO A. ORIENTAÇÃO A OBJETOS Suporte a objetos complexos. Suporte a Identificador de Objetos (OID). Objetos devem ser encapsulados. Suporte a tipos ou classes. Suporte à herança. Acoplamento tardio. Completeza computacional. Extensibilidade. B. SGBD Persistência. Capacidade de armazenamento secundário. Capacidade de prover concorrência. Capacidade de recuperar falhas de software e hardware (recuperação). Consulta simples de dados (queries “ad hoc”). II. CARACTERÍSTICAS OPCIONAIS Suporte à herança múltipla. Suporte a banco de dados distribuídos. Versionamento. Tabela 2 - Características Mandatórias e Opcionais de um SGBDOO. A seguir serão abordadas com detalhes as características mandatórias relacionadas à orientação a objeto e ao SGBD. 2.3.1 – Objetos Complexos Os objetos que contêm outros objetos são chamados de objetos compostos ou complexos, incorrendo em uma hierarquia de composição de objetos. Em determinadas aplicações, um objeto pode participar na composição de muitos objetos. 24 Uma das principais motivações que contribuiu para o desenvolvimento de sistemas de banco de dados orientados a objeto, está relacionada com a dificuldade que sistemas de banco de dados tradicionais têm em representar objetos complexos como imagens, sons, vídeos etc. Há dois tipos de objetos complexos: o estruturado e o não-estruturado. Um objeto complexo estruturado é feito através da composição de elementos, definido através da aplicação de tipos de construtores sobre objetos mais simples ou primitivos (string, float, double etc.) de forma recursiva em vários níveis. Um objeto não-estruturado, tipicamente, é um tipo de dado que requer uma grande quantidade de armazenamento, tais como os tipos de dados que representam uma imagem ou um objeto de texto longo [Elmarsi e Navathe, 2000]. Sets, bags, rows, lists, arrays são exemplos de construtores. O conjunto mínimo de construtores de um sistema é representado por sets (coleções do mundo real), lists ou arrays (captam a ordem que ocorre no mundo real e têm uso em aplicações científicas) e tuplas (representam as propriedades de uma entidade). Esses construtores devem ser ortogonais a ponto de serem aplicados a qualquer objeto [Atkinson, 1989]. 2.3.2 – Identificador de Objetos Quando se cria um objeto, ele recebe um identificador que o diferencia de todos os demais objetos durante sua existência. Nas linguagens orientadas a objeto essa identificação é representada por ponteiros. Como os ponteiros são endereços de memória, eles resolvem muito bem o problema da identificação dos objetos em uma linguagem de programação, porque duram enquanto o programa estiver em execução. 25 Por outro lado, nos SGBDOOs, essa abordagem não pode ser utilizada, pois é necessário que os objetos tenham um tempo de vida maior do que o tempo de execução da aplicação, ou seja, quando a aplicação for executada novamente ela precisa se referenciar a um identificador do objeto que não seja o endereço de memória, devido a sua volatilidade, para poder recuperar o objeto. A identidade do objeto organiza e localiza os objetos no espaço de execução do programa (memória). Entre as diversas formas de representar a identidade do objeto, pode-se destacar a forma em que é atribuído automaticamente um identificador pelo sistema quando o objeto é criado. Esta forma está embutida no modelo de dados ou na linguagem de programação e é usada nos sistemas de orientação a objeto. 2.3.3 – Encapsulamento Na orientação a objeto, a comunicação dos objetos é feita através da troca de mensagens entre os mesmos. Um objeto (emissor) envia uma mensagem para um outro objeto (receptor) a fim de invocar um método do mesmo. A estrutura interna do receptor não pode ser acessada diretamente pelo emissor, somente através de serviços (métodos) disponibilizados pelo receptor. Esse impedimento assegura a integridade do estado do objeto ocultando os detalhes internos do mesmo, caracterizando o encapsulamento do objeto. O encapsulamento significa também que somente os aspectos públicos de um objeto são vistos, enquanto que os detalhes de implementação são ocultos [Rob e Coronel, 1995]. 26 MÉTODO 1 MÉTODO 2 DADOS MÉTODO 3 MÉTODO 4 Figura 4 - Representação gráfica do conceito de encapsulamento. 2.3.4 – Tipos ou classes Existem duas categorias principais de sistemas orientados a objeto: • suportados por tipos; • suportados por classes. Os tipos se relacionam com os tipos de dados abstratos (ADTs -Abstract Data Types), constituindo-se de objetos com características semelhantes. Possuem duas partes: • a interface – trata-se da parte visível ao usuário e consiste de uma lista de operações juntamente com suas assinaturas; • a implementação – é visível apenas ao desenvolvedor , sendo formada por dados de diferentes níveis de complexidade e por operações responsáveis por implementar as outras operações relacionadas à interface. Os tipos são utilizados em tempo de compilação para a checagem de erros dos programas e não podem ser modificados em tempo de execução [Rob e Coronel, 1995]. As classes, assim como os tipos, são constituídos por objetos que possuem as mesmas mensagens, os mesmos métodos e variáveis com mesmo nome e tipo. Desta maneira, os 27 objetos de uma classe são chamados instâncias desta classe. Todavia, as classes são utilizadas para manipular objetos em tempo de execução. 2.3.5 – Herança Conceito no qual uma classe é definida herdando todas as variáveis de instância e métodos de uma classe já existente, adicionando novas variáveis e métodos que diferenciam esta nova classe da existente. Para estabelecer uma hierarquia de classes, é necessário realizar um estudo analítico das classes identificadas, para determinar quais as classes que são potencialmente indicadas para participarem da hierarquia. Após a determinação dessas classes deve-se dividir as variáveis (mesmo nome e tipo), e os métodos e alocá-los em uma outra classe, estabelecendo a hierarquia de classes. Nessa hierarquia de classes, as classes onde foram efetuadas as divisões de variáveis e métodos são chamadas de subclasses e as classes que receberam as variáveis e os métodos em comum são chamadas superclasses. Em uma hierarquia de classes que possui N níveis, ao identificar a estrutura das classes (todo o caminho que “liga” classes de um nível mais alto (superclasse) até uma classe presente em um nível mais baixo (subclasse)), o compilador executa a herança, isto é, a subclasse herda todas as variáveis e métodos de todas as classes superiores a ela no caminho. 28 Figura 5 – Diagrama UML representando herança de classes. Um benefício importante da herança em orientação a objeto é a reusabilidade, visto que os métodos em uma classe não precisam ser redefinidos nas suas subclasses, a menos que uma delas requeira uma implementação distinta. Há duas possibilidades ao associar um objeto a uma classe: • os objetos de uma superclasse são todos os objetos dessa classe em conjunto com todos os objetos de todas as suas subclasses; • os objetos de uma superclasse são todos os objetos dessa classe exceto os objetos de suas subclasses. A escolha mais utilizada nos sistemas desenvolvidos é a segunda possibilidade. 2.3.6 – Acoplamento tardio (late binding) Um objeto pode conter um valor numérico para um determinado atributo, e o próximo objeto da mesma classe pode conter um outro tipo de valor, por exemplo caractere, para o 29 mesmo atributo. Desta forma, tipos de dados de um atributo não são conhecidos até o momento da execução, podendo, duas diferentes instâncias de uma mesma classe conter valores de diferentes tipos para o mesmo atributo. Esta característica é designada de acoplamento tardio [Rob e Coronel, 1995]. Figura 6 Relação de inventário com tipo de dado pré-determinado. Figura 7 - Classe inventário com acoplamento prévio. Figura 8 - Classe inventário com acoplamento tardio. 2.3.7 – Completeza Computacional 30 A completeza computacional está associada à capacidade de executar qualquer tipo de operação em uma determinada linguagem de programação. Nesse sentido, a maioria das linguagens de consulta do modelo relacional são deficientes, pois não são capazes de formular todos os tipos de procedimentos desejados, em razão de não possuírem controle de fluxo, entre outras características. Essa característica pode ser alcançada nas linguagens de programação, devido a sua estrutura permitir a expressão de qualquer tipo de procedimento. Do ponto de vista de um Banco de Dados essa característica é uma novidade, tendo em vista que, por exemplo, a SQL (padrão de linguagem de consulta no modelo relacional) não é computacionalmente completa [Nassu e Setzer, 1999] [Rob e Coronel, 1995]. 2.3.8. – Extensibilidade Todo sistema de banco de dados possui tipos de dados pré-definidos, tais como: strings, float, double, real etc. Extensibilidade é a habilidade de definir novos tipos de dados, o que, geralmente, não é possível em um modelo relacional, mas sim no modelo orientado a objeto. Do ponto de vista do usuário, não deve haver distinção no gerenciamento entre os tipos definidos por ele e tipos definidos pelo sistema [Atkinson, 1989]. 31 2.3.9 – Persistência de Objetos A persistência é uma característica intrínseca de um banco de dados, garantindo que o tempo de vida de um dado perdure mais que o tempo de vida da aplicação, para o mesmo poder ser utilizado posteriormente. Em um SGBD relacional, o dado é representado por relações, que são salvas e recuperadas através de uma linguagem de consulta (SQL). Em um SGBDOO os dados são representados por objetos, e podem ser consultados também por uma linguagem de consulta, como por exemplo, Object Query Language (OQL). Porém, a identificação dos objetos que devem ou não ser persistidos, pode ser realizada através das seguintes técnicas [Silberschatz, 1999]: • Persistência por classe – todas as instâncias criadas a partir de uma classe tornam-se persistentes, se a mesma for declarada como persistente; • Persistência por criação – o objeto é determinado como transiente ou persistente na sua criação; • Persistência por marcação – em qualquer momento do tempo de vida de um objeto pode-se marcá-lo como persistente; • Persistência por referência – designa-se um ou mais objetos como raízes de persistência. Todos os objetos que são referenciados a partir dele tornam-se persistentes automaticamente. 32 2.3.10 – Capacidade de gerenciamento de armazenamento secundário Trata-se da capacidade de utilizar recursos (índices, otimização de consultas entre outros) para o gerenciamento de grandes volumes de dados. Essa coordenação pelo SGBD ocorre de forma transparente para o usuário [Rob e Coronel, 1995]. 2.3.11 – Capacidade de prover concorrência Os SGBDOOs devem dar o mesmo nível de suporte dos SGBDs convencionais no que se refere ao gerenciamento de usuários que acessam concorrentemente o banco de dados (vide item 2.2.3 – Controle de Concorrência). 2.3.12 – Recuperação Segue o mesmo princípio dos sistemas convencionais, devendo o SGBDOO assegurar a recuperação em caso de falhas de software ou hardware (vide item 2.2.4 – Recuperação). 2.3.13 – Consulta simples de dados (queries “ad hoc”) Nesse caso, deve haver uma linguagem de consulta ao banco de dados de forma simplificada que satisfaça os seguintes critérios: • ser uma linguagem de consulta de alto nível: significa que a linguagem deve ser capaz de expressar consultas mais complexas de forma compacta, utilizando-se de pouco código; • ser eficiente: trata-se da capacidade da linguagem formular consultas utilizando um otimizador; 33 • ser uma aplicação independente: trata-se da possibilidade de ser utilizada por quaisquer bancos de dados. 2.4 – PERSISTÊNCIA A persistência de um dado é definida pelo período de tempo em que ele existe e pode ser utilizado [Atkinson, 1983]. Pode ser descrita pelas categorias demonstradas na tabela 3, a seguir: Categoria 1 2 3 4 5 6 7 8 Descrição Resultados transientes de uma expressão Variáveis locais Variáveis globais Dados que duram todo o tempo de execução de um programa Dados que duram por várias execuções de vários programas Dados que duram tanto quanto a utilização dos programas que a utilizam Dados que duram por várias versões de um mesmo programa Dados que duram por várias versões do sistema de persistência Tabela 3 – Tempos de duração dos valores de dados. As categorias de 1 a 4 são definidas como dados transientes ou de período curto e são utilizadas pelas linguagens de programação em geral. As categorias de 4 a 8 são definidas como dados persistentes ou de período longo e são utilizadas por sistemas de arquivos ou sistemas de bancos de dados [Atkinson e Morrison, 1995]. Esta distinção surgiu por causa do tamanho e do custo relativo dos dispositivos de armazenamento de dados. Os sistemas de banco de dados têm como objetivo garantir a persistência dos dados, devendo trabalhar com um meio físico de armazenamento não-volátil. Porém, esse tipo de armazenamento é relativamente lento, e os sistemas de banco de dados têm que ser projetados e estruturados para compensar e minimizar esta característica, a fim de que a mesma não influencie no fornecimento de seus serviços. 34 As linguagens de programação têm como objetivo o processamento dos dados, associando-se com um meio físico de armazenamento rápido. Porém, esse tipo de armazenamento é volátil, e as linguagens de programação não conseguem persistir suas informações mais do que a duração da execução de um programa [Brown, 1988]. Quando uma linguagem de programação quer utilizar dados persistentes, ou seja, necessita transformar seus dados transientes em persistentes para reutilizá-los posteriormente, é preciso recorrer a um banco de dados ou utilizar um meio físico não-volátil de armazenamento. Nesse caso, a persistência está relacionada exclusivamente com um meio físico de armazenamento não-volátil. Finalmente, pode-se entender a persistência de um dado como a transformação de um estado transiente para um estado persistente. 2.4.1 – Persistência Ortogonal A persistência ortogonal é definida através de três princípios: • Ortogonalidade de tipo – todos os dados podem ser persistentes independente de seu tipo; • Independência de persistência – um dado é tratado da mesma forma independente de ser transiente ou persistente; • Identificação de persistência – o mecanismo de identificação de objetos persistentes não é relacionado com o seu tipo. Se nenhum desses três princípios é atendido, a persistência não é ortogonal [Atkinson e Morrison, 1995]. 35 O último princípio é conhecido também como persistência transitiva (termo utilizado pela ODMG – Object Data Management Group) ou também como persistência por referência. A persistência por referência não é somente uma representação desse princípio, mas também sua principal implementação, já que este é o único método de persistência que além de independer do tipo do dado, não causa problemas de referências inválidas ao persistir um objeto e deixar os outros objetos dependentes dele em um estado transiente. A ortogonalidade surge da comparação entre o sentido de utilização do dado e o sentido de persistência do mesmo. Dois sentidos podem ser chamados ortogonais quando entre eles se forma um ângulo reto, ou seja, apesar de se encontrarem em um ponto, eles nunca são contrários, nem opostos. Quando se fala de persistência ortogonal, pode-se dizer que o sentido de utilização do dado é ortogonal ao sentido de persistência do mesmo, porque um não interfere no outro, apesar de atuarem sobre um mesmo ponto, o dado. Em termos gerais pode-se dizer que a persistência é ortogonal quando a mesma é independente da utilização do dado, que é a forma mais desejada de persistência [Atkinson e Morrison, 1995]. 2.5 – LINGUAGENS DE PROGRAMAÇÃO PERSISTENTES A fim de se fornecer persistência dos dados em uma aplicação sem a utilização de um SGBD, pode-se estender uma linguagem de programação para suportar tal persistência. Desta maneira, a linguagem torna-se persistente [Silberschatz, 1999]. Uma linguagem de programação persistente é uma linguagem de programação estendida com estruturas para tratar dados persistentes. A principal filosofia que há por trás dessas linguagens de programação é tornar a persistência ortogonal ao tipo de objeto. Em outras palavras, qualquer tipo de objeto pode ser persistido [Khoshafian, 1994]. 36 Algumas linguagens que suportam a persistência ortogonal são chamadas de linguagens de programação ortogonalmente persistentes, mas pode-se dizer que toda a linguagem que oferece suporte à persistência dentro da própria linguagem é uma linguagem de programação persistente. Porém, esse suporte pode ocorrer em diferentes graus de ortogonalidade e transparência, dependendo de quais princípios da persistência ortogonal são atendidos. É muito difícil atender todos os princípios de persistência ortogonal. Todas as tentativas de implementação tiveram que modificar levemente a própria linguagem ou modificar o ambiente de execução da mesma. Por isso, ao adicionar a persistência ortogonal a uma linguagem, uma nova linguagem é criada. Foi assim com as linguagens S-Algol e Java que ao adicionarem a persistência ortogonal tornaram-se PS-Algol e PJava, respectivamente [Danielsen, 1998]. A maioria das tentativas de se adicionar persistência ortogonal a uma linguagem foi feita com linguagens que suportavam os paradigmas da orientação a objeto. Porém, não necessariamente uma linguagem de programação tem que ser orientada a objeto para ser persistente. Existem outros sistemas que adicionam o suporte à persistência em uma linguagem de programação sem modificar a mesma ou seu ambiente de execução. Isso pode ser feito através de bibliotecas de código geradas na própria linguagem. Tal suporte não cria uma nova linguagem, nem a torna persistente, mas adiciona o suporte de persistência a mesma. 2.6 – INCOMPATIBILIDADE DE IMPEDÂNCIAS (IMPEDANCE MISMATCH) O banco de dados relacional e suas extensões têm sido largamente utilizados na indústria por muitos anos como um padrão para o armazenamento de dados [Ambler, 2003]. Por outro lado, tem crescido a adoção das linguagens orientadas a objeto, assim como as outras tecnologias e ferramentas que envolvem a orientação a objetos na construção de sistemas. 37 A tecnologia relacional suporta o armazenamento de dados em relações e a manipulação destes dados através da linguagem de consulta SQL. A tecnologia orientada a objeto representa um dado como um objeto, e traz diversos paradigmas para se construir aplicações efetuando essa representação. Um sistema tem a necessidade de garantir persistência a seus dados, assim como utilizar uma linguagem que construa a aplicação propriamente dita fornecendo o acesso dos dados ao usuário. Supondo um banco de dados relacional e uma linguagem orientada a objeto, ambos devem trabalhar em conjunto na construção de um sistema. Nessa união, entretanto, encontramos o problema da incompatibilidade de impedâncias. Impedância é uma expressão utilizada em engenharia elétrica e compreende a resistência que um conjunto apresenta a uma determinada carga aplicada sobre ele. A fim de se utilizar o máximo de potência de vários conjuntos que se interligam fazendo parte de um sistema maior, as impedâncias entre eles devem estar casadas, ou seja, serem equivalentes [Irwin, 2000]. Na área de software podemos dizer que os conjuntos em si são as camadas de uma aplicação, devendo se integrar para que o dado transite do usuário até o banco de dados e vice-versa. As impedâncias dessas camadas (a de aplicação orientada a objeto e a de persistência relacional) são incompatíveis porque a representação dos dados entre elas é diferente. Então, nessa área, a incompatibilidade de impedâncias refere-se à inerente diferença que existe entre os modelos de dados relacionais e os orientados a objeto, representando um dado de maneiras diferentes. As implicações resultantes desse problema podem abranger desde o aprendizado de duas linguagens e tecnologias diferentes para se construir um mesmo sistema, até a construção de uma camada de conversão de tipos de dados. Tudo isso para o sistema trabalhar com essas duas tecnologias. Conclui-se, portanto, que para utilizar essas duas tecnologias e resolver o problema de impedância sempre haverá um custo. 38 3 – SISTEMAS PREVALENTES Persistir dados (nesse caso o estado de objetos) sempre foi um problema quando se trata de software orientado a objeto. Com o passar dos anos, tentou-se armazenar objetos de diferentes maneiras, incluindo bancos de dados relacionais, arquivos eXtensible Markup Language (XML) e arquivos texto. Essas abordagens geram sobrecarga no desenvolvimento de software, direcionando esforços para transformar objetos em alguma outra representação de dados para que seu estado possa ser persistido. Essa transformação destrói completamente o conceito de encapsulamento da orientação a objetos, além de não manter o software puramente orientado a objeto [Villela, 2002]. Estima-se que 30% do código gerado em uma aplicação que utiliza um banco de dados como camada de persistência está relacionado à movimentação e organização desses dados e não na modelagem e solução do problema real para o qual o sistema foi proposto [Brown, 1988]. Uma solução para este problema é a prevalência de objetos ou sistemas prevalentes. 3.1 – BREVE HISTÓRICO A prevalência de objetos é um conceito desenvolvido por Klaus Wuestefeld [Wuestefeld, 2001] e colaboradores. Sua primeira implementação, conhecida como Prevayler, tornou-se disponível em novembro de 2001 como um software de código aberto. Atualmente, existem diversas implementações deste conceito em evolução contínua [Villela, 2002]. 3.2 – DEFINIÇÕES DE PREVALÊNCIA Prevalência é um modelo de persistência transparente de objetos em memória. Em um sistema prevalente (que utiliza o modelo de prevalência para persistência de objetos), todos os objetos são mantidos na memória principal do computador. Uma classe específica é utilizada 39 para garantir que todas as alterações efetuadas nesses objetos não sejam perdidas. Consultas são executadas recuperando objetos em memória, tornando seu acesso mais rápido do que através de consultas executadas diretamente em disco [Villela, 2002]. Pode-se analisar o modelo de prevalência comparando-o aos três princípios da persistência ortogonal: independência de persistência, ortogonalidade e identificação de persistência. Entende-se como princípio da independência de persistência, uma escala de transparência que define o grau de diferença no tratamento entre um dado persistente e um dado transiente [Moss e Hosking, 1996]. Um extremo inferior nesta escala pode ser encontrado em aplicações que utilizam um banco de dados relacional como camada de persistência de dados. Nesse caso, a diferença no tratamento desses dados é de tal ordem que, diferentes linguagens têm que ser utilizadas para tratar cada conjunto de dados. Para dados persistentes é utilizada uma linguagem que o banco de dados relacional compreende, como por exemplo SQL, e no caso dos dados transientes a linguagem da própria aplicação. Em um outro extremo, podem-se citar aplicações construídas com linguagens de programação persistentes, que tratam dados transientes e persistentes da mesma maneira. Essa transparência completa é difícil de ser alcançada, pois novas linguagens têm que ser criadas para atender este princípio integralmente. Um nível satisfatório de transparência pode ser encontrado diminuindo-se a diferença no tratamento desses dados até o ponto onde ainda exista um controle em relação à intenção de se trabalhar com dados persistentes ou com dados transientes. Na prevalência, a diferença no tratamento dos dados ocorre na manipulação dos objetos, onde para se inserir ou alterar um objeto persistente deve-se utilizar uma classe específica. Essa técnica de diferenciar o tratamento através de uma classe indica um bom nível de transparência, pois implica diretamente na modelagem da solução e não na 40 codificação. Dessa forma, não existe diferença no tratamento de um dado persistente para um dado transiente no nível de implementação do sistema. O princípio da ortogonalidade trata da independência entre a persistência e o tipo do dado. A ortogonalidade completa também é difícil de ser realizada e pode não ser necessária em alguns casos [Moss e Hosking, 1996]. Algumas estruturas de dados têm sua natureza transiente, e não faz sentido nenhum torná-las persistentes para uma posterior recuperação. É o caso de objetos que são ligados ao contexto de execução de uma aplicação, que somente fazem sentido dentro dela. Objetos que representam canais de acesso a disco, acesso a rede, gerenciamento de memória para a execução do programa, entre outros, só fazem sentido dentro da execução de um programa. Persistir tais objetos pode trazer sérios problemas de implementação [Kienzle e Romanovsky, 2000]. Por outro lado, um alto grau de independência, ou seja, liberdade para poder persistir qualquer classe do domínio do projeto, é muito útil principalmente na modelagem da solução, pois assegura que o modelo de objetos possa ser completo e independente da persistência do dado que está sendo modelado [Atkinson e Morrison, 1995]. Na prevalência, duas restrições são feitas em relação aos tipos de dados que podem ser persistidos ou não: as classes devem ser serializáveis e determinísticas [Oliveira, 2003]. A serialização é a transformação da estrutura do objeto em memória para uma seqüência de bytes, podendo a mesma ser armazenada e depois restaurada ao estado anterior do objeto. No caso da prevalência, ela é o processo que salva o estado do objeto, e deve ser suportada pela linguagem que implementará a prevalência. Os objetos também devem ser determinísticos, ou seja, devem sempre chegar ao mesmo estado quando exposto ao mesmo conjunto de comandos. 41 Essas duas restrições não implicam diretamente em um baixo nível de ortogonalidade, já que dentre as classes de negócio que modelam o problema, e que realmente devem ser persistidas, as duas características podem ser atendidas. O último princípio, o da identificação de persistência, é totalmente atendido pela prevalência, uma vez que tal princípio implementa a persistência por referência. Este tipo de identificação de persistência é realizado incluindo-se no modelo de objetos uma classe que representa a raiz da árvore de persistência. Todos os objetos pertencentes a essa árvore, ou seja, que são referenciados direta ou indiretamente por este objeto, são considerados persistentes. Esse tipo de solução é um pouco intrusiva na modelagem do problema, pois devem ser criadas classes que não representam nenhuma entidade do domínio do problema, mas apenas para proverem a persistência. A prevalência é um modelo de persistência e pode ser implementada em qualquer linguagem orientada a objeto que suporte a serialização de objetos. 3.3 – ESQUEMA GERAL DE FUNCIONAMENTO DA PREVALÊNCIA Como o armazenamento principal dos objetos é feito em um meio físico volátil (memória principal do computador), surge a questão de como a prevalência garante a persistência dos objetos após várias execuções do sistema. Utilizando os mecanismos de log de transação e de backup, a prevalência garante que, mesmo se o sistema for desligado ou parar de executar, nenhum objeto armazenado em memória será perdido. Isso acontece porque os dois mecanismos salvam o estado de um ou mais objetos em um meio físico não-volátil (disco), podendo restaurar o seu valor caso alguma falha ocorra. 42 O log de transação é implementado pela utilização de classes que representam as alterações em objetos, serializando e salvando-as em um meio físico não-volátil. A forma de backup utilizada é o “snapshot”, responsável por salvar o estado de todos os objetos da memória principal para o disco, tirando uma espécie de “fotografia” do estado dos objetos naquele dado momento. A persistência do estado dos objetos só é possível devido ao mecanismo de serialização, que deve ser suportado pela linguagem na qual foi implementada a prevalência. Figura 9 - Serialização do estado de um objeto. A serialização consiste no processo de transformação da estrutura de um objeto em memória para um formato sequencial, o qual pode ser armazenado ou transportado através de uma rede. A sequência de bytes resultante deste processo (figura 9 – dados serializados) pode ser chamada de estado serializado do objeto, pois contém o estado do objeto (valor dos atributos), além de todas as informações necessárias para a reversão do processo. Reconstruir o objeto em memória a partir de seu estado serializado é chamado de desserialização [Strawmyer, 2003]. O objeto não pode ser acessado quando está em seu estado serializado, pois sua estrutura não é mais reconhecida como um objeto, e sim como uma seqüência de bytes. 43 Apesar de terem a mesma função (salvar o estado do objeto em disco), esses mecanismos são complementares devido as suas características e, devem ser utilizados em conjunto para que a persistência seja garantida,pois o “snapshot” pode se tornar lento, caso o modelo de objetos ocupe muito espaço em memória, e utilizá-lo cada vez que o modelo fosse alterado, poderia ter grande impacto no desempenho do sistema. O log de transação, por sua vez, é utilizado toda vez que uma alteração em um objeto persistente é efetuada, gravando a alteração em um arquivo. Com o decorrer das alterações nos objetos do sistema, o arquivo onde é armazenado o log tende a ficar muito maior do que o arquivo onde é armazenado o snapshot, pois várias alterações podem ser feitas em um mesmo objeto sem alterar seu tamanho original. Utilizar o mecanismo de log de transação sem um estado inicial (“snapshot”) pode impactar no desempenho do sistema, pois o tempo de carga e reexecução destas alterações tende a ser muito maior que a carga do arquivo de “snapshot”. Isto se agrava e passa a ser perceptível após um tempo maior de utilização do sistema [Oliveira, 2003]. O log de transação é gravado pelo próprio sistema de prevalência, já o “snapshot” deve ser invocado pela aplicação, em períodos de pouca utilização da mesma. No caso da restauração de uma falha ou reinício da aplicação, o sistema de prevalência procura pelo último “snapshot” gravado, restaurando o estado do sistema para o momento em que a gravação foi feita. A seguir, são reexecutados todos os logs das alterações que foram efetuadas no sistema, posteriores ao “snapshot” tirado, para que o sistema volte ao estado anterior à falha ou ao desligamento do sistema. Embora esses mecanismos utilizem o disco rígido, o armazenamento principal do modelo de prevalência ainda é a memória principal do computador, pois os objetos residem e são 44 manipulados nela. O formato de gravação do objeto em disco é resultante da serialização de seu estado, não podendo ser acessado ou alterado enquanto estiver nesse formato. Enquanto em um SGBD o esquema de recuperação está relacionado à ocorrência de falhas, na prevalência, o log de transação e o “snapshot” funcionam tanto no caso de falhas, quanto no caso de reinício do sistema. Figura 10 - Diagrama temporal do início de execução de um sistema prevalente. 45 Analisando a linha do tempo da figura 10 da esquerda para a direita, é mostrado como acontece o esquema de recuperação. A prevalência procura pelo último “snapshot” gravado em disco e o desserializa, montando os objetos em memória. Em seguida, são localizados e desserializados todos os logs posteriores a esse “snapshot”. Com a reexecução desses logs o sistema volta ao estado consistente anterior a falha. Em um sistema prevalente a persistência começa a ser inserida na modelagem do sistema. Todos os objetos de negócio (objetos que representam entidades diretamente relacionadas ao problema a ser modelado) devem ser referenciados por um objeto raiz (que pode não ser um objeto de negócio) para que a persistência seja garantida. O objeto raiz pode representar o banco de dados do sistema, pois contém a referência a todos os objetos que devem ser persistidos na aplicação. A persistência dos objetos de negócio é garantida através da ligação entre o objeto raiz e o sistema de prevalência, realizada antes de qualquer acesso aos objetos. No caso de um SGBD, essa ligação pode ser representada como a conexão do cliente ao banco de dados. Na modelagem, métodos são criados para implementar regras do sistema, alterando e persistindo o estado dos objetos de negócio. Em uma aplicação orientada a objeto que utiliza um SGBD relacional como camada de persistência, os métodos que persistem os estados dos objetos devem ser implementados invocando comandos SQL (update, insert, delete). Por outro lado, no sistema de prevalência esses métodos têm que ser substituídos por classes que representam alterações nos objetos. Essas classes são serializadas e gravadas em disco antes de serem executadas, assegurando a persistência das alterações e implementando o log de transação. Tais classes recebem o nome de command ou transaction em algumas implementações da prevalência. 46 O funcionamento de um sistema prevalente pode ser resumido pelos seguintes passos: • Uma vez iniciado o sistema, todos os objetos são criados ou carregados em memória, disponibilizando o sistema para uso; • Todas as consultas são feitas procurando os objetos em memória; • Todas as alterações efetuadas nos objetos são representadas por classes serializáveis (transaction ou command); • Cada objeto transaction ou command submetido ao sistema, antes de ser executado é escrito em um arquivo de log; • Quando o sistema é reiniciado, todas as alterações previamente gravadas em arquivos de log são reexecutadas de forma a restaurar seu estado original; • Periodicamente, fotos (“snapshots”) do estado completo dos objetos em memória são gravados em disco. Analisando a linha do tempo da figura 11 da esquerda para a direita, é mostrado como são realizados os mecanismos de log de transação e “snapshot”. A classe command (que representa a alteração de objetos) é serializada e gravada em um arquivo de log antes de cada execução. Em um momento posterior, um “snapshot” é gravado em disco, contendo o estado de todos os objetos em memória. 47 Figura 11 - Diagrama temporal de um sistema prevalente em funcionamento. Algumas regras devem ser seguidas para se criar um sistema prevalente que funcione conforme a descrição anterior: • Todos os objetos que forem persistidos devem ser serializáveis; • Todas as alterações no modelo de objetos devem ser representadas por classes também serializáveis; • O sistema deve ser determinístico, ou seja, o sistema deve sempre chegar ao mesmo estado quando exposto ao mesmo conjunto de comandos [Oliveira, 2003]. 48 3.4 – IMPLEMENTAÇÕES: PREVAYLER E BAMBOO.PREVALENCE A prevalência é um modelo de persistência. Para se construir um sistema prevalente, ou seja, que utiliza a prevalência como camada de persistência, precisa-se de uma implementação concreta para a linguagem a ser utilizada. A pouca documentação existente sobre este modelo, surgiu a partir da primeira implementação para a linguagem Java: o Prevayler [Wuestefeld, 2001]. Essa implementação está atualmente na versão 2.0, e além de implementar todos os requisitos da prevalência também tem características interessantes, como o suporte a rollback. Todas as classes que fazem as alterações nos objetos persistentes implementam uma interface chamada de transaction. Se acontecer algum erro nessas classes (gerados por uma exceção), os comandos de alteração dos objetos são desfeitos. Isso ocorre porque esse mecanismo utiliza uma cópia do modelo de objeto em memória, executando primeiro as alterações nesse modelo, e posteriormente, se nenhum erro ocorrer, aplica as alterações no modelo principal de objetos na memória. Tal mecanismo utiliza o dobro de memória ocupado pelo modelo de objetos [Wuestefeld e Roubieu, 2003]. A versão C#.NET (plataforma de desenvolvimento Microsoft), chamada de Bamboo.Prevalence, foi implementada seguindo o modelo de prevalência do Prevayler e atualmente se encontra na versão 1.4. Dentre as suas principais características podem-se citar a alteração transparente de objetos em memória e um “kit” para a migração da estrutura de objetos (alteração de classes) com o sistema já em produção. A primeira característica possibilita a alteração dos objetos persistentes em memória diretamente através da chamada de seus métodos, sem a necessidade de fazê-la através de uma classe. O próprio sistema de prevalência se encarrega de interceptar a chamada desse método e fazer a gravação do log da alteração em disco. A alteração de objetos através de classes específicas ainda é possibilitada nessa implementação. 49 A segunda característica surgiu da necessidade de manutenção de um sistema prevalente em produção. A questão se relaciona à alteração de sua estrutura, caso necessite realizar alguma manutenção no sistema, pois os objetos estão sempre em memória. Em um sistema que utiliza um banco de dados relacional como camada de persistência, a alteração da estrutura dos dados é feita diretamente no banco de dados, adicionando-se ou removendo-se uma coluna de uma tabela conforme a alteração. Com a nova estrutura de banco de dados, altera-se o programa para refletir as alterações no sistema, e ao se instalar o novo banco de dados e a respectiva versão da aplicação, a manutenção do sistema é realizada. Em um sistema prevalente não existe essa distinção do dado transiente da aplicação e do dado persistente no banco de dados. A estrutura do objeto no código é a mesma do objeto persistente em memória. Diante disso, o Bamboo.Prevalence resolve esse problema através de um “kit de migração” que possibilita a alteração e conversão de estruturas de objetos existentes em memória. Um arquivo XML representa a alteração na estrutura de uma ou mais classes do sistema. Um exemplo da adição de dois novos atributos (X e Y) nos objetos da classe A, localizados no arquivo sistema.dll, pode ser demonstrado através da figura 12. Figura 12 – Arquivo XML que representa a alteração da estrutura de um objeto. 50 O sistema em produção é desligado e um “snapshot” é gravado em disco para que a conversão seja aplicada. A ferramenta de migração intercepta a desserialização desse “snapshot”, aplicando a conversão na estrutura das classes especificadas no arquivo XML. Abaixo são listadas algumas implementações da prevalência de objetos: Implementação Bamboo.Prevalence Common Lisp Prevalence Madeleine Py Per Syst VTN Prevayler Linguagem .NET (C#) Common Lisp Ruby Python Objective-C Endereço na Internet http://bbooprevalence.sourceforge.net/ http://homepage.mac.com/svc/prevalence/readme.html http://madeleine.sourceforge.net/ http://sourceforge.net/projects/pypersyst http://sourceforge.net/projects/vtnprevayler Tabela 4 – Implementações da prevalência em diversas linguagens. As implementações, mostradas na tabela acima, adicionam características à prevalência de acordo com suas próprias necessidades. Não existe um documento formal sobre o que é o modelo de prevalência e quais características devem ser utilizadas a fim de que uma biblioteca de código possa dizer quando adiciona o suporte à persistência na linguagem através da prevalência. Suas versões são atualizadas por seus autores e não têm nenhum correspondente entre si ou com o modelo de prevalência. Todas essas implementações do modelo de prevalência citadas na tabela 4 são softwares de código aberto e podem ser encontradas nos respectivos endereços na internet. 51 3.5 – VANTAGENS E DESVANTAGENS DA PREVALÊNCIA Um dos objetivos da prevalência é tornar o desenvolvimento de software orientado a objeto que necessita de persistência mais simples e mais produtivo. Pode-se comparar a prevalência a outros sistemas que provêem o serviço de persistência para a construção de aplicativos. 3.5.1 – Eliminação da tecnologia relacional A eliminação da incompatibilidade de impedâncias torna-se a principal vantagem quando se faz a comparação entre o sistema de prevalência e o SGBD relacional. Problemas decorrentes dessa incompatibilidade também são eliminados, tais como: • Linguagens diferentes para manipular dados transientes e dados persistentes – utilizando-se de um banco de dados relacional é necessário embutir a linguagem SQL na linguagem da aplicação a fim de se manipular dados persistentes [Atkinson, 1989]. Além de causar ilegibilidade no código, não é possível saber se a sintaxe da linguagem SQL está correta em tempo de compilação, gerando erros que só podem ser percebidos com a aplicação em funcionamento; • Conversão de tipos de dados – os bancos de dados relacionais trabalham com uma representação de tipos de dados diferente da utilizada em uma linguagem orientada a objeto. Para a linguagem de programação garantir a persistência de seus objetos no banco de dados, uma conversão deve ser aplicada. Nesse item, não cabe somente a conversão de tipos primitivos, como números inteiros e decimais, mas também a conversão de objetos em relações. Na transformação de tipos primitivos somente o nome do dado tem que ser alterado. Entretanto, 52 na conversão de objetos em relações, o encapsulamento do objeto tem que ser violado a fim de que seus dados possam ser inseridos em comandos SQL para gravação; • Camada de mapeamento objeto-relacional – por causa das diferenças citadas acima, é necessário implementar código específico para fazer a comunicação entre as duas tecnologias (relacional e orientada a objeto), direcionando esforços na construção de uma camada de persistência dos dados e não na modelagem e solução do problema real para o qual o sistema foi proposto; • Maior aprendizado de tecnologia – a utilização de duas tecnologias diferentes para a construção de um mesmo sistema aumenta o tempo de aprendizado da equipe de desenvolvimento, implicando diretamente no aumento do tempo de construção do sistema [Ambler, 2003]; • Dificuldade de realização de testes automatizados do código – bancos de dados relacionais impõem dificuldades na realização de testes automatizados de um sistema. Não somente na sincronização do código com a estrutura do banco de dados, mas principalmente na dependência de um banco de dados estável e com dados suficientes para se realizar os testes. Isso pode implicar na desistência da elaboração de testes automatizados, resultando na queda de qualidade do sistema [Oliveira, 2003]. A maioria desses problemas podem ser resolvidos com a utilização de um banco de dados orientado a objeto, removendo parcial ou integralmente a incompatibilidade de impedâncias entre a tecnologia relacional e a orientada a objeto. 53 A prevalência ainda é considerada uma tecnologia emergente se comparada aos SGBDs relacionais, existentes há muitos anos no mercado. Os sistemas prevalentes ainda são pouco utilizados no mercado, não provando efetivamente sua maturidade como camada de persistência de dados. A sua pouca idade reflete diretamente no pequeno número de desenvolvedores que conhecem essa tecnologia. 3.5.2 – Ambiente unificado Um SGBD (relacional ou orientado a objeto) é um sistema isolado e independente da aplicação para a qual fornece o serviço de persistência de dados, podendo manter diversos bancos de dados para diversos sistemas em um mesmo SGBD. Essa característica ainda pode gerar os seguintes problemas: • Ambientes diferentes – a aplicação e o SGBD são ambientes separados, requerendo manutenção e administração próprias de cada um; • Conectividade – a separação dos ambientes também gera problemas de conectividade entre os dois sistemas. As aplicações têm que conhecer bibliotecas específicas ou componentes genéricos para acessarem o banco de dados, assim como a instalação de drivers correspondentes aos SGBDs utilizados. Em contrapartida, a remoção do SGBD implica na perda de características muito importantes ao desenvolvimento e à manutenção de um sistema. O programa de interface de acesso e o de armazenamento dos dados devem ficar separados, permitindo que cada cliente utilizando uma interface possa interagir com os dados armazenados. Em um sistema que faz uso de um SGBD para persistir seus dados, a separação é inerente, garantindo a administração e manutenção do armazenamento dos dados de forma transparente. Porém, na prevalência, o 54 processo que armazena os dados deve ser criado pelo próprio desenvolvedor, implicando na elaboração de software adicional para efetivar a manutenção e administração desse processo, assim como o controle do acesso a ele. Outras características como a autorização e a consulta “ad hoc” também são perdidas, obrigando o seu desenvolvimento, se necessário, na própria aplicação. 3.5.3 – Representação única da solução No desenvolvimento de um sistema orientado a objeto que utiliza um banco de dados como camada de persistência, dois modelos têm que ser construídos e mantidos para representar a solução do problema: o modelo de objetos e o modelo do banco de dados. Qualquer alteração no projeto tem que ser refletida e sincronizada nestes dois modelos. A figura a seguir mostra como fica este mapeamento: Figura 13 - Três mapeamentos com duas representações da solução. 55 Supondo um sistema de administração acadêmica que não tenha suporte a controle de mensalidades em uma primeira versão e que, posteriormente o usuário decida adicionar esse serviço. Essa alteração na especificação do sistema (mundo real) obriga conseqüentemente a alterações no programa (implementação de novos programas) e no banco de dados (relações para armazenas as mensalidades). Nesse modelo, o banco de dados, os programas e o problema proposto estão muito acoplados, e qualquer alteração em um deles, como por exemplo, a divisão de uma relação em outras duas por questão de performance, implica diretamente em uma alteração nos outros modelos. Em um sistema prevalente a solução é modelada e mantida em um único modelo de dados, o da própria aplicação, conforme demonstrado na figura a seguir: Figura 14 - Um único mapeamento representando a solução. A eliminação de todos os problemas mencionados conduz a uma redução significativa no tempo de desenvolvimento de um sistema, demonstrando mais uma vantagem no uso da prevalência. 56 3.5.4 – Armazenamento em memória O desempenho do sistema em consultas é melhor do que nos SGBDs devido ao fato do armazenamento ocorrer na memória principal do computador, sendo muito mais rápida do que o acesso a disco. Atualmente, pode se considerar a limitação de tamanho da memória principal uma desvantagem nos sistemas prevalentes, já que esses a utilizam como armazenamento primário de seus objetos. A cada dia tal limitação está diminuindo, mas a defasagem ainda é grande em relação ao armazenamento em disco utilizado pelos SGBDs. 3.6 – APLICAÇÕES POTENCIAIS DE SISTEMAS PREVALENTES Pode-se indicar a prevalência como camada de persistência para os sistemas que tenham os seguintes requisitos: • Pequeno espaço de armazenamento de dados – como a prevalência armazena os objetos na memória principal do computador, esse é o limite de armazenamento do sistema. Aplicações que necessitam de uma rápida expansão no espaço de armazenamento dos objetos para limites além da memória principal, não são indicados para utilizarem a prevalência; • Alta performance de consulta – devido ao fato dos objetos residirem na memória principal do computador, a consulta aos objetos é muito mais rápida na prevalência, caracterizando os sistemas que tem esse como seu principal requisito, os mais indicados a utilizarem a prevalência; 57 • Possuir gerenciamento do processo de armazenamento dos objetos – para sistemas que precisam separar os processos de programa e de armazenamento de objetos para conseguir o acesso multiusuário, só é recomendada a prevalência se existir um sistema externo que garanta o gerenciamento do processo de armazenamento dos objetos. Caso contrário, essa tarefa terá que ser implementada pela própria equipe de desenvolvimento. Nos sistemas em que os objetos e a aplicação permanecem no mesmo processo, mas o acesso multiusuário é garantido por um sistema externo, a prevalência também é indicada. Um tipo de aplicação que se enquadra nos requisitos acima são websites que tenham um grau de dinamicidade muito alto em seu conteúdo. Esse tipo de aplicação requer um alto nível de consulta ao banco de dados, pois para cada acesso às páginas do website, no mínimo um acesso ao banco de dados deve ser feito para montar a página a ser exibida. Websites com conteúdo dinâmico não requerem muito espaço de armazenamento de dados, pois além de possuírem poucas entidades de negócio, somente é armazenado o conteúdo dinâmico e não as páginas inteiras do site. Um servidor web cuida do gerenciamento do processo e do provimento de acesso multiusuário dos dados, suprimindo essa tarefa da equipe de desenvolvimento. Uma outra característica da prevalência é o aumento de produtividade no desenvolvimento de aplicações, que dependendo do tipo de aplicação requerida, essa vantagem pode abranger um número maior de tipos de aplicações. Quando as limitações de memória principal não forem mais um problema e a prevalência estiver mais madura, uma gama muito maior de tipos de aplicações poderá ser alcançada. Sistemas que atingirão seu limite de armazenamento em alguns anos, já utilizam a prevalência, apostando que o armazenamento em memória RAM não será mais um problema quando esse limite for alcançado. 58 4 – IMPLEMENTAÇÃO 4.1 – PRÉ-REQUISITOS Para construir um sistema prevalente em Microsoft® .NET os seguintes softwares são requeridos: • Microsoft® .NET Framework Software Development Kit (SDK) 1.1 – inclui todas as ferramentas necessárias para o desenvolvimento de aplicações em Microsoft® .NET. Pode ser conseguido gratuitamente no endereço: http://www.microsoft.com/downloads/details.aspx?FamilyId=9B3A2CA63647-4070-9F41-A333C6B9181D&displaylang=en. Também é possível utilizar a versão 1.0, mas no exemplo de implementação utilizado nesse estudo será utilizada a versão 1.1. • Bamboo.Prevalence Microsoft® .NET 1.3 que – implementação pode ser da conseguida prevalência no em endereço: http://sourceforge.net/project/showfiles.php?group_id=61693&release_id=10 9035. Pode ser utilizada uma versão mais atualizada, mas neste exemplo de implementação será utilizada a versão 1.3. O Microsoft® .NET Framework Software Development Kit (SDK) 1.1 contém o compilador C# que será utilizado para gerar o sistema prevalente em .NET. A implementação da prevalência em .NET foi desenvolvida em C# mas pode ser implementada em qualquer outra linguagem suportada pela plataforma Microsoft® .NET, como por exemplo Microsoft® VB.NET ou Microsoft® JScript.NET. 59 Um sistema prevalente é uma aplicação em .NET que fornece suporte a persistência de modo transparente. Como qualquer sistema orientado a objeto o primeiro passo a ser seguido para o seu desevolvimento é a realização de sua modelagem. Atualmente a plataforma de desenvolvimento Microsoft® .NET executa somente em sistemas operacionais Windows (98 ou superior). Existe uma iniciativa de se portar essa plataforma de desenvolvimento para o ambiente operacional UNIX, habilitando assim a prevalência em Microsoft® .NET em outros sistemas operacionais. Isso, entretanto, não está hoje disponível. 4.2 – DESENHO DA APLICAÇÃO Aqui desenvolvemos um sistema de controle acadêmico com as seguintes funcionalidades: • Manutenção de alunos – permite a inclusão, alteração e exclusão de um aluno e busca pelo seu nome; • Manutenção de cursos – permite a inclusão, alteração e exclusão de um curso; • Manutenção de etapas – permite a inclusão, alteração e exclusão de uma etapa; • Efetivação de matrícula por etapa e curso – permite matricular um aluno em um curso/etapa, criando um código de matrícula para o mesmo. 60 O modelo do sistema é representado pelo diagrama da figura 15. Figura 15 - Modelo de objetos. A classe Aluno contém um código para a criação da matrícula, além dos atributos Nome e Endereco (um outro objeto). Esse código é utilizado na efetivação da matrícula, fazendo a ligação entre o Aluno, o Curso e a Etapa através do objeto Matricula. Todas as classes, com exceção de Endereco, possuem um atributo chamado Id que é a identificação do objeto. Esse atributo é artificial, ou seja, não está relacionado com o domínio do problema, e foi criado para obter uma referência aos objetos persistentes em execuções posteriores do sistema. Como o tempo de vida dos objetos é maior que o da aplicação, deve-se utilizar um identificador de objetos que também seja persistente, para o mesmo poder ser referenciado fora do modelo de objetos, como por exemplo, na interface com o usuário. A classe Endereco não possui um identificador de objetos persistente, pois somente é acessada através da classe Aluno, que já apresenta um identificador de objetos persitente. Esses identificadores de objeto são únicos para cada objeto criado no sistema, diferenciando um objeto de outro da mesma classe. Pode-se fazer uma analogia com a chave 61 primária do modelo relacional, que também identifica unicamente uma entidade dentro de uma relação no banco de dados. O valor do identificador de objetos deve ser controlado pelo sistema para que nenhuma repetição aconteça. Também se pode utilizar um tipo de dado que, ao ser criado, não gere nenhum valor repetido, o que violaria a regra de identificação do objeto. Para as classes Etapa e Curso, o atributo identificador do objeto é do tipo integer (valores entre – 2.147.483.648 e 2.147.483.647). Esse tipo de dado não garante a unicidade de seu valor, havendo a necessidade da criação de um mecanismo de controle que não permita a repetição do identificador para objetos diferentes. No caso das classes Etapa e Curso, o controle da criação desses valores foi centralizada na classe SystemDatabase, que cria identificadores únicos de objetos ao torná-los persistentes. Diferentemente de uma modelagem relacional, pode-se utilizar além dos tipos convencionais já existentes no sistema (long, integer etc.), classes criadas externamente. Na classe Aluno, o identificador de objetos utilizado é de um tipo específico pertencente à plataforma Microsoft® .NET, o GUID. Essa classe cria um valor único cada vez que é instanciada, não necessitando de um controle externo que evite a repetição do identificador de objetos para a classe Aluno. 4.3 – AMBIENTE WEB Implementamos uma aplicação web. A figura 16 traz esquematicamente como ela está organizada. As páginas web forneceram a interface ao cliente, que pode acessá-las e interagir com a aplicação. Os web browsers fazem as requisições ao servidor, que executa um programa correspondente (acessando os objetos) e devolve uma resposta ao usuário. O 62 servidor web é responsável por gerenciar tanto o acesso multiusuário quanto a manutenção do processo onde residem os programas e os objetos do sistema, motivo pelo qual escolheu-se esse modelo. Caso contrário, seria necessária a construção de código adicional para prover a comunicação entre diversos clientes e o sistema prevalente. Como o objetivo deste estudo é a utilização da prevalência para adicionar persistência a um sistema orientado a objetos, e não aplicações web, é suficiente o detalhamento aqui apresentado. Figura 16 - Ambiente de uma aplicação web. 4.4 – ADICIONANDO A PREVALENCIA AO MODELO O modelo atende todas os requisitos propostos, mas se o sistema for executado os objetos serão perdidos quando a execução for interrompida (o modelo não inclui bases de dados), ou seja, os objetos ainda não persistem. 63 O primeiro passo para adicionar a prevalência é criar uma classe que irá referenciar todos os objetos que devem persistir no sistema (objeto raíz da persistência). Pode-se fazer uma analogia desta classe a um banco de dados, pois todos os objetos persistentes são referenciados e encontrados a partir dela. Uma instância dessa classe deve ser conectada à biblioteca da prevalência quando o sistema for iniciado. Figura 17 - Modelo de objetos com suporte à prevalência. No modelo da figura 17, uma instância da classe SystemDatabase (que será criada pela biblioteca de prevalência) é o objeto raíz da persistência que contém vetores que referenciam os objetos persistentes. Pode-se utilizar vetores ou qualquer outro tipo de estrutura de dados para referenciar os objetos persistentes, inclusive a referência direta aos objetos persistentes. Como no sistema um dos requisitos é armazenar mais de um objeto persistente da mesma classe, uma estrutura do tipo vetor será utilizada para referenciar essas instâncias, pois novas referências podem ser adicionadas. 64 A classe SystemDatabase se utiliza de uma classe ArrayList do próprio framework .NET, representando um vetor de objetos com crescimento dinâmico. Além disso, fornece algumas funções utéis, como por exemplo, a busca binária dentro deste vetor. Analogamente pode-se comparar esses vetores a relações de um banco de dados relacional, pois armazenam dados de um mesmo tipo. 4.5 – IMPLEMENTANDO A PREVALÊNCIA Após implementar as classes de negócio, deve-se garantir que todas essas classes sejam serializáveis, a fim de que os mecanismos de snapshot e log de transação possam ser aplicados e, se algum erro ocorrer, os objetos possam ser recuperados. Na plataforma de desenvolvimento Microsoft® .NET , a serialização é feita automaticamente desde que a classe contenha o atributo [Serializable], conforme o exemplo a seguir: [Serializable] public class Aluno { private String _name; private Matricula _matricula; private Endereco _address = null; private System.Guid _id; public Aluno() { _id = System.Guid.NewGuid(); _address = new Address(); } } Exemplo 1 – Serialização automática com o uso do atributo [Serializable]. Em uma aplicação web Microsoft® .NET a função Application_Start, como mostra o próximo código, é executada antes da aplicação estar disponível para os usuários a fim de fazer a conexão entre a biblioteca de prevalência e o objeto raiz de persistência. 65 protected void Application_Start(Object sender, EventArgs e) { 1 PrevalenceEngine engine = PrevalenceActivator.CreateTransparentEngine (typeof(SystemDatabase), @"c:\data"); 2 SystemDatabase sysdb = engine.PrevalentSystem as SystemDatabase; 3 this.Context.Application.Add("SystemDatabase", sysdb); 4 this.Context.Application.Add("PrevalenceEngine", engine); } Exemplo 2 – A função Application_Start faz a conexão entre a biblioteca de prevalência e o objeto. Na linha 1 (como destacado abaixo) é criado o objeto que representa o mecanismo da prevalência (engine). Este objeto possui as principais funções da prevalência, como a execução de classes de comandos (command) e de snapshots. PrevalenceEngine engine = PrevalenceActivator.CreateTransparentEngine (typeof(SystemDatabase), @"c:\data"); Exemplo 3 – Criação do objeto engine. Na criação desse objeto é passado como parâmetro o tipo da classe que representa o objeto raiz da persistência (SystemDatabase) e o caminho onde serão gravados os “snapshots” e os logs de transação. Na linha 2 o objeto engine cria o objeto raiz da persistência que é referenciado pela variável SysDB. Nesse momento é verificado se existe algum “snapshot” e/ou logs de transação gravados em disco (c:\data). Caso exista, a recuperação dos dados é efetuada. SystemDatabase sysdb = engine.PrevalentSystem as SystemDatabase; Exemplo 4 – Criação do objeto raiz da persistência. As duas últimas linhas (3 e 4) adicionam esses objetos ao contexto da aplicação, tornando-os disponíveis a todas as páginas e classes da aplicação, para que as mesmas possam 66 utilizar-se das funções destes objetos, como por exemplo, executar uma classe de comando (serviço do objeto engine) ou acessar um objeto persistente (serviço ao sysdb). this.Context.Application.Add("SystemDatabase", sysdb); this.Context.Application.Add("PrevalenceEngine", engine); Exemplo 5 – Adicionando objetos ao contexto da aplicação. A memória do sistema em execução pode ser ilustrada através da figura 18. Figura 18 - Objetos persistentes e transientes. 67 Nessa figura todos os objetos persistentes são referenciados pelo objeto raiz da persistência. Os objetos que não são apontados por ele são transientes, ou seja, serão destruídos quando a execução da aplicação terminar. Quando um objeto é criado ele primeiramente é transiente pois é referenciado por uma variável (identificador de objeto) que também é transiente. Para torná-lo persistente, basta adicionar uma referência a ele no objeto raiz da persistência. A troca do estado do objeto (de transiente para persistente) deve ser feita através de uma classe de comando (command), assim como alterações em objetos persistentes, para que as mesmas não sejam perdidas se o sistema parar de executar. 4.6 – MANIPULANDO OBJETOS PERSISTENTES Utilizando a biblioteca de prevalência Bamboo.Prevalence, pode-se exemplificar uma classe de comando através do código abaixo: [Serializable] public class Command : ICommand { . . . } Exemplo 6 – Exemplo de uma classe de comando. A classe deve implementar a interface ICommand porque é através dela que a biblioteca de prevalência consegue invocar um método específico para a execução do comando (Execute). Antes da execução do comando, um log é gravado em disco, que é a serialização da própria classe de comando. Quando o sistema reinicia, essa classe é desserializada e executada novamente para que a alteração não seja perdida. Todas as operações (inclusão, alteração, exclusão) que alteram o estado de qualquer objeto persistente, inclusive do objeto raiz da persistência, devem ser feitas por classes de comando. 68 Operações que não alteram o estado de nenhum objeto persistente (consultas), devem ser feitas por classes de consulta (não são serializadas), que implementam a interface IQuery: public class Query : IQuery { . . . } Exemplo 7 – Exemplo de uma classe de consulta. 4.6.1 – Inclusão No caso de uma inclusão, a classe de comando deve alterar o objeto raiz de persistência, adicionando uma referência para o novo objeto. No exemplo 8, a seguir, demonstra-se a classe de comando que inclui um objeto da classe Curso: [Serializable] public class AdicionaCursoComando : ICommand { Curso _curso = null; public AdicionaCursoComando (Curso curso) { _curso = curso; } public object Execute(object system) { ((SystemDatabase)system).AdicionaCurso(_curso); ((SystemDatabase)system).ListaCurso.Sort(); return null; } } Exemplo 8 – Exemplo de uma classe de inclusão. O código que traduz o objetivo do comando está dentro do método Execute. Na classe do exemplo 8, esse método transforma um objeto transiente em persistente, chamando um método da classe SystemDatabase que cria um identificador para o novo objeto e depois o insere no vetor correspondente. 69 Como essa classe tem que ser serializada, o atributo [Serializable] deve constar antes do nome da classe. O código cliente que invoca este comando para tornar um objeto do tipo Curso persistente pode ser visualizado no exemplo 9: 1 Curso curso = new Curso(txtNome.Text); 2 AdicionaCursoComando comando = new AdicionaCursoComando(curso); 3 ExecuteCommand(comando); Exemplo 9 – Código que invoca o comando que torna o objeto do tipo Curso persistente. Na linha 1 do exemplo 9, cria-se um objeto do tipo Curso a partir de um controle que contém o nome do curso digitado pelo usuário na tela do sistema. Por enquanto esse objeto é transiente e seu identificador é a variável curso. 1 Curso curso = new Curso(txtNome.Text); A segunda linha cria uma instância da classe AdicionaCursoComando passando o objeto curso como parâmetro. 2 AdicionaCursoComando comando = new AdicionaCursoComando (curso); A última linha invoca o método ExecuteCommand do objeto engine para executar o comando. Antes de ser executado o objeto é serializado em disco. O diagrama de sequência da figura 19 mostra como a inclusão de um novo curso ocorre: 70 Figura 19 - Diagrama de sequência da inclusão de um objeto persistente. Essas classes de alteração (comando) devem ser utilizadas também para que um mecanismo de bloqueio (lock) seja realizado no sistema. Todas as classes de alteração que implementam ICommand são executadas unicamente no sistema, ou seja, nenhum outro comando é executado até que o corrente termine, garantindo a consistência do modelo de objetos em memória. Pode-se dizer que a execução dos comandos é serializada, ou seja, somente é executado um comando por vez no sistema. Esse mecanismo é importante para atualizações, mas é prejudicial para a performance do sistema se for utilizado para consultas, já que uma consulta não altera o estado de nenhum objeto no sistema. Desse modo, várias consultas podem ser realizadas em paralelo sem que o modelo de objetos fique em um estado inconsistente. [Oliveira, 2003]. 71 4.6.2 – Consulta As classes que funcionam como consultas no sistema, devem implementar a interface IQuery. Essas classes não são serializadas justamente porque não alteram o estado de nenhum objeto persistente. Para cada consulta a ser realizada em um ou mais classes persistentes, deve-se criar uma classe de consulta correspondente. Não se deve acessar um objeto diretamente para consulta, pois a classe de consulta implementa um mecanismo de bloqueio de leitura, isto é, a biblioteca de prevalência verifica se não existe nenhuma outra classe de alteração (comando) sendo executada. Caso exista, a consulta não pode ser realizada enquanto o comando não terminar, pois a classe de consulta pode visualizar um estado inconsistente do modelo de objetos. Deste modo, a biblioteca de prevalência garante que várias classes de consulta possam ser executadas paralelamente caso nenhum comando estiver em execução. Em síntese, uma classe de alteração bloqueia a execução de todas os outras classes, independentes se elas são de consulta ou de alteração, mas uma classe de consulta não bloqueia a execução de outras classes de consulta [Oliveira, 2003]. 72 No exemplo 10, a seguir, é mostrado um exemplo de uma classe de consulta: [Serializable] public class AchaCursoConsulta : IQuery { long _id; public AchaCursoConsulta (long Id) { _id = Id; } public object Execute(object system) { return ((SystemDatabase)system).AchaCurso(_id); } } Exemplo 10 – Código que cria uma classe de consulta. O comando de consulta, mostrado no código anterior, busca pelo identificador de objeto no vetor respectivo dentro do objeto raiz de persistência (método FindCourse da classe SystemDatabase faz uma busca binária dentro do vetor CourseContainer). A seguir retorna uma cópia do objeto para o cliente que executa este comando, a fim de que o objeto seja exibido na tela, por exemplo. Um código cliente que invoca essa classe de consulta pode ser demonstrado no código de exemplo 11, abaixo: 1 AchaCursoConsulta consulta = new AchaCursoConsulta (Id); 2 Curso curso = ExecuteQuery(consulta); Exemplo 11 – Código que invoca uma classe de consulta. A primeira linha cria um novo objeto do tipo AchaCursoConsulta (que é uma classe de consulta) passando o identificador do objeto a ser localizado (Id). 1 AchaCursoConsulta consulta = new AchaCursoConsulta(Id); A segunda linha executa o comando de consulta e retorna o objeto encontrado. 73 2 Curso curso = ExecuteQuery(consulta); Se não for encontrado nenhum objeto com o identificador passado, é retornado o valor null para o objeto curso. Dessa maneira, esse código pode exibir uma mensagem para o usuário de objeto não encontrado. O diagrama de seqüencia a seguir ilustra a execução dessa classe de consulta no sistema: Figura 20 - Diagrama de sequência da consulta de um objeto persistente Através do diagrama, pode-se perceber que não existe serialização da classe de consulta AchaCursoConsulta. 4.6.3 – Alteração Para a alteração de um objeto persistente, o mesmo deve ser localizado antes de ser alterado. O código a seguir, exemplo 12, representa uma classe que faz a alteração em um objeto do tipo Curso persistente: 74 [Serializable] public class AtualizaCursoComando : ICommand { Curso _curso; public AtualizaCursoComando (Curso curso) { _curso = curso; } public object Execute(object system) { Curso cursoPersistido = ((SystemDatabase)system).AchaCurso(_curso); cursoPersistido.Nome = _curso.Nome; return null; } } Exemplo 12 – Código da classe de alteração do objeto Curso. Dentro do método Execute, temos a chamada do método AchaCurso do objeto raiz de persistência, para encontrar um objeto que tenha o mesmo identificador do objeto passado como parâmetro (_curso). Esse objeto transiente deve ter o identificador preenchido assim como todos os valores dos atributos que foram alterados. Como a classe Curso tem somente a propriedade nome além do identificador, somente é necessário fazer uma cópia do valor do atributo nome do objeto transiente alterado para o persistente. O código cliente que invocaria esta alteração está descrito a seguir, exemplo 13: 1 2 3 4 Curso curso = new Curso(Id); RecebeDadosdaTela(curso); AtualizaCursoComando comando = new AtualizaCursoComando(curso); ExecuteCommand(comando); Exemplo 13 – Código de alteração da classe Curso. Esse código é uma atualização em um objeto curso já existente, ou seja, antes deste código ser executado o objeto a ser alterado foi localizado e exibido na tela do sistema. Quando o usuário alterou os dados e clicou no botão salvar, este código é invocado. 75 A primeira linha cria um objeto do tipo Curso com um identificador de objeto válido, ou seja, o identificador do objeto persistente que foi exibido anteriormente na tela, com a variável curso apontando para ele. 1 Curso curso = new Curso(Id); A segunda linha tem um método (RecebeDadosdaTela) que atualiza o objeto curso com todos os valores dos atributos que foram alterados na tela do sistema. 2 RecebeDadosdaTela(curso); As duas últimas linhas criam uma instância da classe (AtualizaCursoComando) de comando que faz a alteração de um objeto do tipo Curso, e invoca o método ExecuteCommand passando esta instância. 3 AtualizaCursoComando comando = new AtualizaCursoComando(curso); 4 ExecuteCommand(comando); O diagrama de seqüência a seguir, ilustra como é a execução da alteração de um objeto curso persistente: 76 Figura 21 - Diagrama de sequência de alteração de um objeto persistente. 4.6.4 – Exclusão A exclusão de um objeto no sistema é feita através da execução de uma classe de comando que remove a referência do objeto raiz de persistência para o objeto a ser excluído. Nesse exemplo de implementação, o objeto a ser excluído é armazenado em um vetor de objetos e a exclusão é efetuada, tornando o objeto transiente. A classe de comando (RemoveCourseCommand) que remove um objeto do tipo Course do sistema é mostrada no exemplo 14, a seguir: [Serializable] public class ExcluiCursoComando : ICommand { long _id; public ExcluiCursoComando(long id) { _id = id; } 77 public object Execute(object system) { ((SystemDatabase)system).ListaCurso.Remove(_id); return null; } } Exemplo 14 – Código da classe de comando que remove o objeto do tipo Curso. O código cliente que chama este comando é mostrado no exemplo 15, a seguir: 1 ExcluiCursoComando comando = new ExcluiCursoComando (Id); 2 ExecuteCommand(comando); Exemplo 15 – Código cliente que chama o comando que remove o objeto do tipo Curso. A primeira linha cria um comando que remove um objeto do tipo Curso do sistema, passando o identificador do objeto a ser removido (Id). 1 ExcluiCursoComando comando = new ExcluiCursoComando (Id); A segunda linha executa o comando. 2 ExecuteCommand(comando); O diagrama a seguir ilustra essa execução: 78 Figura 22 - Diagrama de sequência de exclusão de um objeto persistente. As operações realizadas nas outras classes do sistema são muito semelhantes às operações apresentadas nesta seção, razão pela qual não foram demonstradas neste trabalho. 4.7 – DESEMPENHO Com base no exemplo de implementação, foram desenvolvidos testes que avaliaram o tamanho do processo que armazena os objetos e do “snapshot” gravado em disco, assim como o desempenho da busca de objetos em um sistema prevalente. Os testes abrangeram as seguintes tarefas: • Inclusão de n objetos; 79 • Ordenação do vetor de objetos; • “Snapshot” dos objetos; • Busca pelo primeiro objeto do vetor; • Busca pelo último objeto do vetor. Para fins de avaliação de desempenho, o log de transação foi desabilitado, pois o seu uso implicaria no aumento do tempo de inserção, dificultando a realização dos testes. Os testes foram executados 3 vezes para os valores de n igual a 10.000, 100.000 e 1.000.000 de objetos. A tabela 5 apresenta os resultados obtidos. n = 10.000 objetos Tarefa Teste 1 7.430.144 Tamanho do processo em memória (bytes) 31,25 Busca do primeiro objeto do vetor (ms) 15,625 Busca do último objeto do vetor (ms) 3,796875 Tempo total de execução (s) 1.232 Tamanho do snapshot em disco (Kbytes) n = 100.000 objetos Tarefa Teste 1 24.047.616 Tamanho do processo em memória (bytes) 46,875 Busca do primeiro objeto do vetor (ms) 31,25 Busca do último objeto do vetor (ms) 24,265625 Tempo total de execução (s) 12.306 Tamanho do snapshot em disco (Kbytes) n = 1.000.000 objetos Tarefa Teste 1 178.274.304 Tamanho do processo em memória (bytes) 31,25 Busca do primeiro objeto do vetor (ms) 31,25 Busca do último objeto do vetor (ms) 297,828125 Tempo total de execução (s) 123.048 Tamanho do snapshot em disco (Kbytes) Teste 2 7.430.144 15,625 15,625 3,875 1.232 Teste 3 7.430.144 31,25 31,25 3,828125 1.232 Teste 2 24.047.616 46,875 15,625 24,15625 12.306 Teste 3 24.047.616 15,625 31,25 22,953125 12.306 Teste 2 177.967.104 46,875 31,25 296,625 123.048 Teste 3 178.274.304 31,25 31,25 297,765625 123.048 Tabela 5 - Resultados dos testes de desempenho. A tabela 5 mostra que tanto o espaço utilizado em memória para armazenar os objetos (tamanho do processo) quanto o espaço utilizado em disco para armazenar o estado serializado dos objetos (tamanho do “snapshot” em disco), cresceram proporcionalmente em relação ao 80 número de objetos inseridos. Como somente o estado dos objetos (e mais algumas informações a respeito das classes) é armazenado em disco, o tamanho do espaço utilizado em memória é maior que o espaço utilizado em disco. A grande variação encontrada nos tempos de busca dos objetos, mesmo entre os testes executados com o mesmo número de objetos, indica que esses valores não podem ser considerados absolutamente como indicadores de performance para a busca de objetos em memória. Devido ao valor muito baixo do tempo de busca (alguns milisegundos), diversas interferências (ou até mesmo a falta de precisão na medição) podem ter causado uma variação tão grande (em alguns casos essa diferença chega a 100%). Esses indicadores podem ser analisados relativamente, comparando-se os tempos de busca de objetos realizado em domínios diferentes. A tabela 5 mostra que o tempo de busca de um objeto entre 10.000 é praticamente o mesmo de uma busca efetuada em um domínio de 1.000.000 de objetos. Isso demonstra que o desempenho de busca não é degradado pelo aumento no número de objetos, provando não ser proporcional ao crescimento do sistema. 81 5 – CONCLUSÃO Comparada com outras tecnologias de persistência, a prevalência tem características que a aproximam mais de uma linguagem de programação persistente (LPP) do que de um SGBD. Em uma escala de transparência, a persistência fornecida pela prevalência só não se iguala às LPPs, que conseguem atingir o nível máximo. As LPPs, entretanto, tem um alto custo de implementação, pela modificação da linguagem ou de seu ambiente de execução, o que não ocorre no caso da prevalência. A prevalência tem uma portabilidade melhor para linguagens orientadas a objeto do que as LPPs, pois deve-se apenas reescrever a bilblioteca na linguagem destino, ao invés de se criar uma nova linguagem. A portabilidade da prevalência não é difícil de ser realizada, pois seu código não é extenso. No desenvolvimento de sistemas orientados a objeto, a utilização da prevalência como camada de persistência é mais produtiva que o SGBDR, pois elimina a incompatibilidade de impedâncias. A prevalência, porém, é limitada a orientação a objeto, diferentemente dos SGBDRs que podem atender tanto as linguagens procedurais quanto as orientadas a objeto. Os SGBDOOs também eliminam a incompatibilidade de impedâncias, mas não promovem uma integração completa da aplicação com o repositório de objetos como no ambiente unificado da prevalência. Observa-se também que a utilização da prevalência permite reduzir os custos no desenvolvimento de sistemas orientados a objeto, eliminando licenças de uso, hardware e equipe de profissionais especializados na administração e gerenciamento de SGBDs. Esta redução, porém, implica na perda de suporte do fabricante, já que a prevalência é um software livre de código aberto. 82 O conceito que mais se assemelha na prevalência a uma transação dos SGBDRs é o de classes de comando, pois podem agrupar diversas operações em uma única tarefa. Quanto à propriedade de atomicidade, a prevalência somente apresenta esta característica mediante a utilização de um mecanismo de rollback, disponível apenas em algumas de suas implementações e não utilizada neste trabalho. A consistência das transações são garantidas no sistema através da serialização da execução de classes de comando, não permitindo execuções paralelas. Desse modo, o sistema garante que nenhuma classe de comando irá visualizar o estado do sistema enquanto outra estiver sendo executada, pois o sistema pode estar em um estado inconsistente. A propriedade de isolamento é uma consequência direta desta serialização. Quanto à propriedade de durabilidade, a prevalência garante a persistência de objetos no sistema. Uma linguagem orientada a objeto que implementa a prevalência não necessita de um SGBD para persistir seus objetos, embora existam outros serviços necessários ao desenvolvimento de sistemas, como o gerenciamento do acesso multiusuário e do armazenamento dos dados, que são fornecidos por um SGBD. Dessa forma, um sistema prevalente que necessite desses serviços deve implementá-los ou confiar seu fornecimento a outro software, como por exemplo, um servidor de aplicacões ou um servidor web. De acordo com o exemplo de implementação, a utilização da prevalência demostrouse bastante produtiva, reduzindo o tempo de desenvolvimento do sistema. Se fosse utilizado um banco de dados relacional, haveria a necessidade de se elaborar um segundo modelo para representar os dados, enquanto que na prevalência a única visão existente dos dados é o próprio código. Além disso, a utilização de um SGBDR requer a construção de código 83 adicional para solucionar o problema de representação dos dados, existente entre as tecnologias orientada a objeto e relacional. O fato da prevalência utilizar somente uma tecnologia, a orientada a objeto, assim como o SGBDOO, habilita o desenvolvedor a trabalhar em um ambiente totalmente orientado a objeto, evitando um mapeamento implícito no desenvolvimento, que ocorre quando é utilizado um SGBDR. Esta característica permite novas possibilidades de implementação de soluções, antes inviáveis com o uso de um SGBDR. Para ilustrar uma possibilidade, no exemplo de implementação foi possível atribuir um tipo novo para o atributo chave de uma classe do sistema, que na realidade era outra classe criada no próprio sistema. Isso não limita a escolha de tipos de atributos das classes somente aos tipos existentes no sistema. Em um sistema prevalente, o acesso direto aos objetos sem a utilização de classes de comando, ou de classes de consulta, é muito facilitado, pois tanto objetos transientes quanto persistentes estão disponíveis da mesma forma. Isso pode induzir a um erro na utilização do sistema, pois o acesso direto não serializa as alterações efetuadas, podendo ser perdidas se o sistema parar de executar antes de um “snapshot”. Cabe ao desenvolvedor atentar para esse fato. A simplicidade de se adicionar a prevalência no sistema foi comprovada pela adição apenas de uma classe ao modelo de objetos e pela fácil conexão desta classe com a biblioteca de prevalência. Na codificação essa simplicidade ficou menos evidente, pois foi necessária a criação de classes de comando para efetuar alterações em objetos persistentes. Os resultados obtidos pelos testes comprovaram o alto desempenho na consulta de objetos fornecida pela utilização da prevalência. O esforço de busca demonstrou-se não ser proporcional ao número de objetos existentes no sistema, já que a busca de um objeto em uma lista de 10.000 apresentou um desempenho semelhante a uma busca realizada em uma lista de 1.000.000 de objetos. 84 Por se tratar de uma tecnologia emergente, várias questões relacionadas ao desenvolvimento de sistemas precisam ainda ser investigadas e avaliadas. Entretanto, já existem algumas aplicações que utilizam, com sucesso, essa tecnologia. Essas aplicações se caracterizam principalmente por atenderem os requisitos de baixo volume de armazenamento, alto desempenho de consulta e baixo tempo de desenvolvimento do sistema. Dentre esses casos, podemos citar o website da emissora de televisão Rede Record (www.rederecord.com.br) que utiliza a prevalência para armazenar todo o conteúdo dinâmico do website, incluindo notícias, descrição de programas, realização de enquetes, grade de programação etc. A evolução da tecnologia de armazenamento de memória volátil pode minimizar uma das principais limitações da prevalência, possibilitando assim a sua maior utilização no desenvolvimento de sistemas orientados a objeto. 85 6 – REFERÊNCIAS BIBLIOGRÁFICAS AMBLER, S. Agile Database Techniques: Effective Strategies for the Agile Software Developer. Nova Iorque: John Wiley & Sons, 2003. ATKINSON, M. P. et al., An Approach to Persistent Programming. Computer Journal, v. 26, n. 4, 1983. Disponível em: <http://www.dcs.stand.ac.uk/research/publications/download/ABC+83a.pdf>. Acesso em: 20 out. 2003. ATKINSON, M. P. et al., The Object-Oriented Database System Manifesto. In Proc. of the 1st Int. Conf. on Deductive and Object-Oriented Databases, Kyoto, Japão, 1989. Disponível em: <http://citeseer.ist.psu.edu/atkinson89objectoriented.html>. Acesso em: 01 nov. 2003. ATKINSON, M.P. e MORRISON, R. Orthogonally Persistent Object Systems. VLDBJournal, Pacific Grove, v. 4, n. 3, 1995. Disponível em: < http://citeseer.nj.nec.com/atkinson95orthogonally.html >. Acesso em: 10 nov. 2003. BROWN, A. L. Persistent Object Stores. Tese (Ph.D thesis) - Computational Science, University of St. Andrews, 1988. Disponível em: < http://citeseer.nj.nec.com/brown88persistent.html >. Acesso em: 15 out. 2003. DATE, C.J. Introdução a Sistemas de Banco de Dados. 8 ed., Rio de Janeiro: Campus, 1991. DANIELSEN, A. The Evolution of Data Models and Approaches to Persistence in Database Systems. University of Oslo, 1998. Disponível em: <http://www.fing.edu.uy/inco/grupos/csi/esp/Cursos/cursos_act/2000/DAP_DisAvDB/doc umentacion/OO/Evol_DataModels.html>. Acesso em: 23 out. 2003 ELMARSI, R. e NAVATHE, S. Fundamentals of Database Systems. 3 ed., Vancouver: Addison-Wesley, 2000. IRWIN, J. Análise de circuitos em engenharia. 4 ed., São Paulo: Makron Books, 2000. KHOSHAFIAN, S. Banco de Dados Orientado a Objeto. Rio de Janeiro: Livraria e Editora Infobook S.A.,1994. KIENZLE, J. e ROMANOVSKY, A. A Framework based on Design Patterns for Providing Persistence in Object-Oriented Programming Languages. Newcastle: University of Newcastle, 2000. Disponível em: <http://citeseer.nj.nec.com/kienzle00framework.html>. Acesso em: 12. nov. 2003. MOSS, J. e HOSKING, A. Approaches to adding persistence to Java. In: First International Workshop on Persistence and Java, Drymen, Escócia, 1996. Disponível em: < http://citeseer.nj.nec.com/moss96approaches.html >. Acesso em: 23 out. 2003. NASSU, E. e SETZER, V. Bancos de Dados Orientados a Objetos. São Paulo: Editora Edgard Blücher Ltda, 1999. 86 OLIVEIRA, R. B. (Autor da camada de prevalência para CLI – ECMA 335). Comunicação pessoal, 2003. ROB, P. e CORONEL, C. Database Systems – Design, Implementation and Management. 2 ed., Danvers:International Thomson Publishing Company, 1995. SILBERSCHATZ; A.; KORTH, H. e SUDARSHAN, S. Sistemas de Banco de Dados. 3 ed., São Paulo: Makron Books, 1999. STRAWMYER, M. Serialization/Deserialization in .NET, 2003. Disponível em < http://www.developer.com/net/csharp/article.php/3110371>. Acesso em: 20 nov. 2003. VILLELA, C. An introduction to object prevalence, 2002. Disponível em: <http://www106.ibm.com/developerworks/web/library/wa-objprev/ 01/06/2003>. Acesso em: 17 out. 2003. WUESTEFELD, K. Prevayler, 2001. Disponível em: <http://www.prevayler.org/wiki.jsp>. Acesso em: 10 jul. 2003 WUESTEFELD, K. e ROUBIEU, J. Prevayler: Objetos Java Invulneráveis. Mundo Java, Curitiba, n. 1, pp. 25-31, set.-out. 2003. 87 7 – BIBLIOGRAFIA COMPLEMENTAR ARAÚJO, N. Jr. Banco de Dados: Passado, Presente e Futuro. Developers’ Magazine, Rio de Janeiro, n. 47, p. 13, jul. 2000. FILGUEIRAS, E. Híbridos: ORDBMS são viáveis para OODBMS ? Developers’ Magazine, Rio de Janeiro, n. 47, pp. 16-17, jul.-2000. GAMMA, E. Design Patterns: Elements of Reusable Object-Oriented Software . Vancouver: Addison-Wesley Pub Co, 1995. KIM, W. Modern Database Systems – the object model, interoperability and beyond. New York: Alan Press, 1995. KROENKE, D. M. Database Processing – Fundamentals, Design & Implementation. 9 ed., New Jersey: Prentice Hall, 2003 MENDONÇA, S. e SAPIENZA, S. SGBDOO: Uma realidade próxima ou um futuro distante? Developers’ Magazine. Rio de Janeiro: n. 47, p. 21, julho-2000. OLIVEIRA, R. B. Bamboo.Prevalence - a .NET object prevalence engine. Disponível em: <http://bbooprevalence.sourceforge.net/10/08/2003>. Acesso em: 12 ago. 2003. O’NEILL, P. e O’NEILL, E.. Database-Principles Programming Performance. 2 ed., San Francisco: Morgan Kaufmann Publishers, 2001. RICCARDI, G. Principles of Database Systems with Internet and Java Apllications. Boston: Addison-Wesley, 2001. STONEBRAKER, M. readings in... database systems., 3 ed., San Francisco: Morgan Kaufmann Publishers, 1998. ZANIOLO, C. et al. Advanced Database System. San Francisco: Morgan Kaufmann Publisher Inc, 1997. 88