Apache Cayenne: Persistência de dados

Fique por dentro
Este artigo é útil por apresentar o Apache Cayenne, um mapeador objeto-relacional não aderente à
especificação JPA. Integrado a uma poderosa ferramenta gráfica – o Cayenne Modeler – esta solução da
Apache agrega agilidade e simplicidade ao desenvolvimento da camada de persistência de dados.
Sendo assim, desenvolvedores que buscam soluções voltadas para a persistência de dados podem encontrar
no Apache Cayenne uma eficiente alternativa de ORM que abstrai qualquer necessidade de lidar com
arquivos XML ou anotações durante o trabalho de mapeamento.
A necessidade de persistir dados em tempo de execução é tão antiga quanto a própria computação. De fato, a
abordagem de gerenciar dados persistentes tem sido uma peça fundamental de decisão na concepção de todo
projeto de software.
Notadamente, a aplicabilidade de um sistema cujos dados manipulados não sejam salvaguardados em algum
formato ou estrutura contemplaria um escopo tão reduzido que a própria existência do sistema poderia ser
objeto de questionamento.
Na perspectiva da plataforma Java, a persistência normalmente está associada ao armazenamento de dados
em bancos de dados relacionais que fazem uso da linguagem SQL. No contexto deaplicações orientadas a
objetos, a persistência possibilita que um objeto sobreviva à finalização do processo que o criou, uma vez
que seu estado pode ser armazenado em disco, o que viabiliza a sua posterior recriação.
Naturalmente essa operação não é limitada a apenas um único objeto – todo um grafo de objetos pode da
mesma forma ser persistido e subsequentemente recriado por meio de um novo processo.
Atualmente, grande parte dos projetos de softwares corporativos fazem uso conjugado do paradigma da
orientação a objetos – para o desenvolvimento de aplicações – e da persistência em bancos de dados
relacionais – para o armazenamento das informações.
Contudo, as diferenças existentes entre os fundamentos dessas duas abordagens inviabilizam uma perfeita
adequação no uso concomitante de ambas. Isto porque enquanto o paradigma da orientação a objetos é
baseado em princípios advindos da Engenharia de Software, o paradigma relacional, por outro lado, é
baseado em fortes princípios matemáticos.
Logo, para que as duas abordagens possam coexistir em um mesmo projeto de sistema, é preciso preencher
essa lacuna semântica (conhecida como Impedance Mismatch) que separa os dois paradigmas.
Uma técnica encontrada para contornar as especificidades que separam o mundo dos objetos do mundo das
relações ficou conhecida como mapeamento objeto-relacional (Object-Relational Mapping – ORM). Em
poucas palavras, um mapeamento objeto-relacional é um mecanismo de persistência baseado na tradução de
objetos de uma aplicação para tabelas de um banco de dados relacional por meio de metadados que
descrevem o mapeamento entre os objetos e o banco. Em essência, uma solução de ORM consiste em quatro
peças principais:
· Uma API para executar operações básicas de CRUD (Create-Read-Update-Delete) sobre objetos de
classes persistentes;
· Uma linguagem ou API para especificar queries que possam referenciar classes e suas respectivas
propriedades;
1

· Uma forma de se construir mapeamentos por meio de metadados;
· Um mecanismo para que a implementação do ORM possa interagir com objetos transacionais a fim de
executar a otimização de funções, como checagem de inconsistência de dados ou recuperação lazy de
associações.
Alguns dos benefícios que podem ser derivados do uso de um mecanismo de ORM são:
· Produtividade: Boa parte do código relacionado com a camada de persistência de uma aplicação é
substituída pelas funcionalidades da solução de ORM. Logo, desenvolvedores podem focar ainda mais nos
problemas referentes ao negócio para o qual a aplicação está sendo construída;
· Manutenibilidade: Uma menor quantidade de linhas de código torna um sistema mais simples de se
compreender, uma vez que o código existente está focado na lógica de negócio. Ainda mais importante é o
fato de que sistemas com menos linhas de código são mais fáceis de serem refatorados.
Assim, dado que a persistência automatizada por uma solução de ORM reduz a quantidade de linhas de
código de um sistema, então dela pode-se obter os ganhos citados anteriormente;
· Desempenho: Aclama-se que um código de persistência escrito diretamente pelo desenvolvedor da
aplicação tem um desempenho pelo menos igual e, com frequência pode ter um desempenho superior,
quando comparado ao código gerado e executado por um mecanismo de automatização de persistência.
Contudo, quando uma tarefa de persistência é analisada, sabe-se que diversas otimizações são passíveis de
serem efetuadas. Algumas delas talvez sejam mais simples de implementar por meio de códigos construídos
pelos próprios programadores.
Porém, muitas dessas otimizações são mais fáceis de serem alcançadas por meio de soluções automatizadas.
Além disso, equipes que implementam ORM provavelmente já desprenderam muito mais tempo
investigando métricas de desempenho do que o tempo que qualquer desenvolvedor teria para estudar
possíveis otimizações;
· Independência de fornecedores: Um mecanismo de ORM abstrai da aplicação as particularidades do
banco de dados e do dialeto SQL usados. Se a ferramenta suporta um número diferente de bancos de dados,
isso confere um certo nível de portabilidade à aplicação.
Logo, o desenvolvimento de um sistema multiplataforma torna-se usualmente mais simples quando há a
utilização de uma solução de ORM. Além disso, ainda que operações multiplataforma não constituam um
requisito para o sistema, o ORM pode auxiliar na mitigação de alguns dos riscos associados à dependência
de fornecedores específicos.
Diante dos benefícios advindos da automatização das operações de persistência, diversas implementações de
mecanismos de ORM surgiram. Na perspectiva da tecnologia Java, boa parte delas segue a especificação
JPA.
Dentre os mais conhecidos estão Hibernate, TopLink e EclipseLink. Neste artigo, será apresentado uma
solução de ORM que não segue a especificação JPA, o Apache Cayenne. Para exemplificar o uso da
ferramenta, esta será utilizada para implementação da camada de persistência de uma aplicação web de
exemplo.

2

Nota: A JPA (Java Persistence API) foi desenvolvida por um grupo de especialistas em EJB 3.0 como parte
da JSR 220, porém não está limitada a componentes EJB. Ela provê um modelo de persistência de POJO que
dá suporte ao desenvolvimento de aplicações independentes de soluções particulares de ORM.
O Apache Cayenne
O Apache Cayenne é um framework open source que provê um mecanismo de mapeamento objetorelacional, além de serviços remotos auxiliares. Iniciado em 2001 e desde então, mantido pela comunidade,
o projeto Cayenne possibilita a redução do tempo de desenvolvimento de aplicações cuja persistência é
baseada em modelos relacionais, uma vez que cria uma abstração – orientada a objetos – das estruturas
existentes em esquemas de bancos de dados.
Sua versão estável mais recente é a 3.0.2 (versão usada neste artigo). Porém, conta ainda com uma release
candidate – versão 3.1 – e uma opção mais atual que encontra-se em fase de testes, a versão 3.2. Algumas
das suas principais características são:
· Suporte tanto à engenharia reversa quanto à geração de bancos de dados;
· Engine de geração de classes baseada no Velocity;
· Mecanismo de caching;
· Completo conjunto de objetos voltados para a sintaxe de queries;
· Herança de objetos;
· Autodetecção de banco de dados;
· Capacidade de escalar projetos de qualquer dimensão para ambientes virtuais.
Notadamente, diversas características implementadas pelo Cayenne são comuns a outros mecanismos de
ORM. Contudo, um relevante diferencial disponibilizado nesta solução é a possibilidade de ter todas as suas
funções controladas diretamente via uma ferramenta com interface gráfica – o CayenneModeler.
De fato, esta última isenta o desenvolvedor da necessidade de lidar com quaisquer arquivos XML ou
anotações durante a etapa de mapeamento objeto-relacional. Ressaltando ainda mais o potencial da
ferramenta, por meio do CayenneModeler é possível que um completo esquema de banco de dados seja
mapeado diretamente para objetos Java via interface gráfica.
Outra característica que distingue o framework Cayenne dos demais mecanismos de ORM relaciona-se com
a sua arquitetura, a qual pode ser vista como a composição de duas grandes soluções de persistência:
· Cayenne Persistence API: API de propósito geral para mapeamentos objeto-relacionais. Internamente
utiliza arquivos XML para descrição dos mapeamentos, porém estes arquivos podem ser abstraídos do
desenvolvedor por meio da utilização da interface do CayenneModeler;
· Remote Object Persistence (ROP): É uma tecnologia que habilita o uso da API de persistência do
Cayenne em aplicações-cliente remotas. Um uso comum do ROP é oferecer a aplicações SWT ou Swing um
web service para acesso aos dados, em substituição ao acesso direto ao banco de dados.
Em casos como esses, um modelo abstrato de dados é compartilhado entre os dois lados – cliente e servidor.

3

contains(jm)).contains(jm)).apache.getListOfRevista(). jm. Por default. o Cayenne disponibiliza funcionalidades voltadas para o gerenciamento dos objetos persistíveis: · Execução de queries: queries podem ser invocadas a fim de recuperar objetos que atendam a certos critérios. a entidade B tem um relacionamento com a entidade A. assertTrue(CayenneModeler. Essa interface – uma extensão da interface java. Teste unitário para demonstração do gerenciamento de relacionamentos bidirecionais efetuado pelo Cayenne. esse recurso simplifica o gerenciamento de complexos grafos de objetos. A Listagem 1 dá uma visão do efeito que esse gerenciamento tem sobre o código. Listagem 1. assertTrue(cayenne.contains(jm)). · Relacionamentos: O suporte a relacionamentos possibilita que objetos referenciados por um objeto recuperado possam ser acessados via uma única chamada simples de método. jm. os objetos referenciados são recuperados quando o tamanho da lista é solicitado ou quando o usuário tenta acessar um de seus elementos. os relacionamentos não são recuperados juntos com os objetos. · Injeção de Contexto: O framework Cayenne executa a atribuição de todas as três propriedades (objectId. assertFalse(CayenneModeler. · Múltiplos níveis de Commit e Rollback aninhados: As funcionalidades de commit e rollback podem ter múltiplos níveis de aninhamento. Naturalmente. isto é. em um relacionamento TO-MANY.getListOfRevista(). Artigo CayenneModeler. 4 .setArtigo(CayenneModeler). Nota: O Cayenne é capaz de persistir objetos Java que implementem a interface org. Artigo cayenne.Além do mecanismo de persistência propriamente dito. Revista jm. Isso automatiza a manutenção dos estados de persistência quando um objeto sofre alguma transformação em seu estado.io.Serializable – requer que cada objeto proveja os métodos acessores (get/set) para três propriedades do bean: objectId.getListOfRevista().setArtigo(cayenne). além disso. De forma análoga. a execução do commit das alterações de um contexto não persiste no banco de dados as modificações do “contexto-pai”. o Cayenne mantém a consistência do relacionamento reverso automaticamente. · Chamada a um único método de Commit e Rollback: múltiplos objetos persistentes podem ter suas alterações gravadas no banco de dados por meio de uma única chamada de commit e dentro de uma única transação.Persistent. persistentState e objectContext. um contexto pode executar o rollback de suas modificações sem afetar o “contexto-pai”.cayenne. Logo. Outro aspecto é que objetos persistentes podem ser criados e preenchidos com valores provenientes do banco de dados. persistenceState e objectContext) definidas na interface Persistence em momentos adequados do ciclo de vida dos objetos. · Gerenciamento automático de relacionamentos bidirecionais: Se uma entidade A tem um relacionamento com a entidade B e.

são definidos seis estados possíveis para um objeto: · TRANSIENT: o objeto não está registrado no contexto de persistência e não está persistido na base. no máximo. Os valores serão automaticamente carregados pelo Cayenne se alguma propriedade do objeto for requisitada. · COMMITED: o objeto está registrado no contexto de persistência e já está persistido na base (há um registro no banco correspondente). o objeto foi modificado na memória e suas alterações não foram persistidas na base. · HOLLOW: o objeto está registrado no contexto de persistência e tem um registro correspondente na base. o Cayenne executa um carregamento do tipo lazy dos seus dados. Transições entre estados ocorrem em resposta à interação da aplicação com os objetos persistentes – modificação de atributos. isto é. Em outras palavras. apenas a chave-primária (objectId) é recuperada e atribuída. Porém. · NEW: o objeto foi recentemente criado no contexto de persistência. Porém. Já a Figura 2 descreve as transições possíveis para objetos recuperados de um banco de dados ou salvos nele. No contexto de persistência do Cayenne. porém não está persistido na base (não tem um registro no banco correspondente). a mesma instância do objeto será usada para ambos os resultados. quando este tipo de objeto for manipulado pelo usuário (um método set/get é invocado). A Figura 1 apresenta as mudanças de estado que um novo objeto pode sofrer dentro do contexto do Cayenne. usada em clientes remotos. As duas implementações mais usadas são: DataContext. · Recuperação lazy de objetos: Para objetos que são recuperados via relacionamentos. usada na maioria das aplicações Cayenne. os valores ainda não foram recuperados do banco.· Exclusividade de identidade: o Cayenne assegura que cada ObjectContext contenha. o Cayenne automaticamente popula os demais atributos com os dados do banco. uma instância de cada objeto persistente. Ciclo de vida dos objetos O ciclo de vida de objetos persistentes no Cayenne pode ser representado por um diagrama dos estados possíveis dos objetos e das transições que podem existir entre eles. Nota: ObjectContext é uma interface que define a API para a aplicação interagir com o Cayenne. · DELETED: o objeto está registrado no contexto de persistência e será removido da base após o próximo commit. se duas queries independentes requisitam um registro com a mesma chave primária. · MODIFIED: o objeto está registrado no contexto de persistência e tem um registro correspondente na base de dados. 5 . por exemplo – ou com o próprio contexto de persistência do Cayenne – inclusão e remoção de objetos do contexto. Este comportamento (não suportado por alguns frameworks) é extremamente importante para a manutenção da consistência do grafo de objetos. a qualquer momento. e CayenneContext. Contudo.

Transições de objetos persistentes dentro do contexto do Cayenne. Figura 2.Figura 1. Transições de um novo objeto no contexto de persistência do Cayenne. E como o conceito de chave-primária é um aspecto de persistência do modelo relacional 6 . Gerenciamento de chaves primárias As diferentes estratégias de geração de chaves-primárias geralmente estão associadas aos respectivos bancos de dados existentes.

objetos persistíveis pelo Cayenne têm um subconjunto de seus atributos utilizados como chave primária. Algumas das abordagens disponíveis no Cayenne são: · Chaves primárias de negócio: Nessa abordagem. Essa estratégia pode ser especialmente útil quando a chave primária tem significado no domínio do negócio. os desenvolvedores ficam isentos da responsabilidade de lidar diretamente com a chave primária no modelo de objetos da aplicação. o valor da chave é automaticamente derivado pelo Cayenne a partir da chave primária do objeto referenciado pelo relacionamento. Todos os parâmetros do comando são informados como atributos da classe. porém cujo tipo é outro DataObject (ou coleção de DataObjects). Esse cenário geralmente ocorre em casos nos quais join tables são usados para resolver relacionamentos MANY-TO-MANY. · Chaves primárias providas por bancos de dados: Há casos em que o banco de dados é dotado do seu próprio mecanismo de geração de chaves primárias quando um novo registro é inserido. Este aspecto é conhecido como “auto incremento” ou “coluna identidade”. Logo. o qual é gerenciado pelo próprio mapeador. Nestes casos. Ao contrário. As principais classes que implementam essa abstração e disponibilizam queries para propósitos gerais nas aplicações são: · SelectQuery: É uma classe Java que implementa o comando SELECT da linguagem SQL. Por sua vez. Para isentar os desenvolvedores dessa responsabilidade. Para a maioria das tarefas com o Cayenne. é recomendável considerá-la como a primeira opção para a implementação das queries. o DataObject normalmente não contém uma propriedade mapeada para uma coluna de chave primária. o Cayenne define para cada objeto um atributo ObjectId. Normalmente ela constitui apenas um número sequencial que identifica um registro no banco de dados. Nota: DataObjects são objetos passíveis de serem persistidos pelo Cayenne. Os atributos dos DataObjects são simplesmente as propriedades dos objetos (como propriedades de um bean Java) que podem ser mapeadas para colunas de um banco de dados. a chave primária é puramente um conceito relacional que não tem correspondente no modelo de objetos. Objetos desse tipo são compostos por atributos e relacionamentos. Na prática. 7 . · Chaves primárias derivadas de relacionamentos: A chave primária de uma coluna pode depender da chave de outra tabela. um relacionamento também constitui uma propriedade do DataObject. Neste caso.que alcança o mundo dos objetos. Assim. Dessa maneira. a PK é tratada pelo Cayenne da mesma forma que qualquer outro atributo persistente. tanto atributos como relacionamentos podem ser lidos ou modificados através da invocação dos métodos get/set correspondentes. a criação de um atributo num objeto persistente a fim de corresponder à chave primária pode ser vista como um passo artificial no processo de mapeamento. essa diversidade de estratégias tem impacto nas abordagens de geração de PKs implementadas pelos mecanismos de ORM. O Cayenne suporta este tipo de provimento externo de chave primária. os relacionamentos representam outros DataObjects que são referenciados por um objeto persistível. a manipulação dos dados da base é feita por meio de objetos Java que abstraem o dialeto SQL usado pelo gerenciador de banco de dados. · Chaves primárias geradas pelo Cayenne: Na maioria dos casos. Queries No Cayenne.

· SQLTemplateQuery: É uma classe Java que possibilita a execução de queries escritas apenas com recursos da linguagem SQL.xml. obtido via JNDI. Existem. o Cayenne permite o mapeamento e execução de stored procedures. Naturalmente. Estrutura geral de um projeto Cayenne A implementação de uma camada de persistência baseada no Cayenne demanda a manipulação de estruturas que implementam conceitos específicos do framework. tanto classes Java como tabelas e visões do banco de dados são considerados entidades. De fato. Atualmente existem dois tipos de DataNodes: o O baseado em um data source. o uso de SQLTemplateQuery é voltado para a criação de queries complexas. as quais não são possíveis de serem construídas via modelo de objetos. 8 . Em sua maioria. · Entities: No Cayenne. Algumas dessas classes são as seguintes: · QueryChain: Classe Java que referencia um conjunto de outras queries. o Cayenne provê seu próprio pool de conexões e outras funcionalidades esperadas de um data source. os quais são descritos nos DataMaps. Essa funcionalidade complementar é disponibilizada por meio da classe ProcedureQuery. quando nenhum registro é encontrado no banco. Essas estruturas dão suporte à construção tanto do modelo relacional como do modelo de objetos. o E outro baseado em um driver JDBC. dois subtipos de Entity. · ProcedureQuery: Adicionalmente ao mapeamento de tabelas e visões de bancos de dados para entidades do modelo de objetos. normalmente um banco de dados. O objetivo dessa classe é possibilitar a execução de múltiplas queries em uma única chamada ao banco de dados. · DataNode: É um objeto que corresponde a um único data source físico. podendo contemplar múltiplos bancos de dados físicos. Esse tipo de objeto possibilita ainda a adaptação do código SQL para diferentes dialetos de bancos de dados. · ObjectIdQuery: Classe Java que possibilita a recuperação de objetos baseando-se apenas no atributo ObjectId. Os principais elementos do Cayenne com essa finalidade são: · DataDomain: Constitui uma abstração de um data source lógico. como o ObjectId é sempre único para cada objeto. o Cayenne oferece um conjunto de classes para simplificar ainda mais a manipulação dos dados da base. porém. é nele onde são definidas as correspondências entre objetos e estruturas do banco de dados. · DataMap: É uma coleção de informações de mapeamentos objeto-relacionais que vincula objetos Java a tabelas e visões de um banco de dados relacional. A relação de todos os DataDomains presentes num projeto pode ser encontrada no arquivo cayenne. um DataMap contém o mapeamento objetorelacional propriamente dito. ou seja. ou seja. São eles: o ObjEntity: Entity que representa a estrutura de uma classe persistente Java. o resultado obtido pela classe ObjectIdQuery é composto de um único objeto ou é nulo. tanto elementos do mundo dos objetos como elementos do mundo relacional são tratados como Entities. Neste caso. bem como à criação do mapeamento entre eles. Além das principais queries mencionadas. · EJBQLQuery: Classe Java que dá suporte à implementação de queries cuja sintaxe segue o padrão EJBQL descrito na especificação JPA.

xml: Arquivo que provê as informações necessárias para conexão com o banco de dados: URL. DataNode e DataMap – constitui uma abstração dos três tipos de arquivos que são usados para configurar o mecanismo de ORM numa aplicação. porém podem existir DbEntities que não têm um ObjEntity correspondente.xml: Constitui o “arquivo-raiz” do Cayenne em cada aplicação. Outras funcionalidades do CayenneModeler são: · Engenharia reversa de esquemas de banco de dados convertendo-os para DataMaps. 9 . possibilita que todo o processo de mapeamento objeto-relacional possa ser executado sem qualquer contato com arquivos ou anotações. os quais descrevem o esquema do banco de dados e seu mapeamento para as classes Java da aplicação.o DbEntity: Entity que contém a representação da estrutura de uma tabela/visão do banco de dados. o CayenneModeler oferece uma interface gráfica em Java que dispensa essa necessidade de interação direta com os arquivos XML. que abrange três conceitos centrais do Cayenne – DataDomain. Vale notar que cada ObjEntity é baseado em um DbEntity. dessa forma. · Geração de esquemas de bancos de dados a partir de informações de mapeamentos armazenadas em DataMaps. Estruturas básicas de um projeto Cayenne.xml: Arquivo usado para instanciar objetos DataMap.map. o uso de composições dentro de entidades.driver. há uma descrição desses arquivos (a variável $DataDomainName é usada para representar o nome do projeto Cayenne): · cayenne. · {$DataDomainName}Node. Figura 3. O CayenneModeler Embora os arquivos de configuração gerados por um projeto Cayenne possam ser editados manualmente. Além disso. JDBC driver. Nele são configurados quais os DataDomains e DataNodes que devem ser reconhecidos pelo framework. A Figura 3 mostra uma visão geral de como um projeto Cayenne é estruturado. · {$DataDomainName}Map. Esse esquema. usuário. · Embeddables: É um tipo especial de objeto persistente que permite o mapeamento de partes de uma tabela para um objeto separado. senha e parâmetros do pool de conexões. possibilitando. A seguir.

exe disponibiliza a interface principal da ferramenta. acesse a página oficial do Apache Cayenne (veja a seção Links). este constitui uma poderosa ferramenta que agrega simplicidade e agilidade à atividade de mapeamento objeto-relacional. 10 . A aplicação Amanaje Com o objetivo de consolidar os conceitos apresentados do Apache Cayenne e de observar como o framework pode ser empregado em sistemas web. Como o intuito é dar uma visão geral do uso do ORM oferecido pelo Cayenne. para manter o foco centrado no mapeador.zip do Cayenne. Diante das facilidades oferecidas pelo CayenneModeler. Nota: Segundo o pesquisador Gerson França. Interface inicial do CayenneModeler. Para obtê-lo. o objetivo da aplicação Amanaje é o de reunir um conjunto básico de funcionalidades a fim de demonstrar o uso da persistência de dados via Apache Cayenne. Com base nesse conceito. Figura 4.· Geração de classes Java a partir das informações presentes em DataMaps. o executável do CayenneModeler se encontra no diretório bin gerado após a descompactação. alguns critérios de arquitetura de sistemas e boas práticas no desenvolvimento de software foram abstraídos. Uma vez baixado o arquivo . a qual é apresentada na Figura 4. apenas um CRUD (CreateRead-Update-Delete) foi implementado na aplicação. · Validação e criação de mapeamentos. ou “ajuntamento”. a palavra amanajé (de origem tupi) significa “reunião”. foi desenvolvida uma aplicação – chamada de Amanaje – cuja interface disponibiliza um conjunto simplificado de funcionalidades. Além disso. A chamada ao arquivo CayenneModeler.

xsd"> <modelVersion>4. Como o relacionamento entre as duas classes é do tipo M:N.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>com. Nesse sentido.apache.0.apache. conforme apresentado na Figura 5.0. Além disso.0.1. utilizou-se o JBoss AS 6.0</modelVersion> <groupId>br. · TipoQualificacao: representa os diferentes níveis de qualificação que um usuário pode ter.0" xmlns:xsi="http://www.0 http://maven.org/POM/4. para deploy e execução da aplicação.1.apache. foi desenhado para a aplicação um modelo de entidade-relacionamento que fizesse uso de uma tabela auxiliar – a Join Table qualificacao – a fim de agregar as chaves das duas entidades.0. · model: Pacote que agrupa o modelo de objetos.A ideia geral da aplicação Amanaje é a de uma interface para manutenção de um cadastro de usuários e de suas respectivas qualificações. Tratando-se da arquitetura geral da aplicação. Arquivo pom. utilizou-se o gerenciador de banco de dados PostgreSQL.7</version> <scope>provided</scope> </dependency> <dependency> 11 . Para fins de simplificação tanto da gerência de dependências quanto da construção das páginas web. foi feita uma organização do código em três pacotes: · dao: Pacote que contém os Data Access Object da aplicação. versão 8.xml relacionando as bibliotecas demandadas pela aplicação.w3.beans: Pacote onde são encontrados os controladores das páginas web.4.org/xsd/maven-4.0.jm</groupId> <artifactId>amanaje</artifactId> <version>0.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven. foram projetadas duas classes para compor o modelo de objetos: · Usuario: representa o próprio usuário cujas qualificações serão associadas. Para a persistência dos dados. Listagem 2. a aplicação foi criada como um projeto Maven fazendo uso do framework JSF 2. · web.0.sun.org/POM/4.xml da aplicação Amanaje. Diagrama de Entidade-Relacionamento da aplicação Amanaje.1. A Listagem 2 apresenta o código do arquivo pom. Figura 5. <project xmlns="http://maven.

Uma vez executada essa operação. ao efetuar o salvamento do novo projeto. através do menu Project >DataNode (opcionalmente. Finalmente. logo. deve-se acessar o menu File > New Project e. será delegado ao Cayenne a responsabilidade de criação e atualização do esquema.0RC2</version> </dependency> <dependency> <groupId>postgresql</groupId> <artifactId>postgresql</artifactId> <version>9. esta é referenciada pelo artifactId cayenne-server. Neste artigo.apache. já é possível avançar ao mapeamento objetorelacional. contendo as informações para acesso ao banco de dados. 12 . pode-se ter acesso a essa funcionalidade via barra de ferramentas – botão “Create DataNode”). a ferramenta solicitará a indicação do diretório no qual o arquivo cayenne. E no caso da biblioteca que fornece o mecanismo de persistência do Cayenne.jdbc4</version> </dependency> </dependencies> </project> Vale observar nesta listagem que as duas dependências referentes ao JSF têm escopo provided. Em seguida. uma vez que ambas serão fornecidas pelo container em tempo de execução.xml será criado. Naturalmente. Assim. é disponibilizada uma tela na qual os dados de configuração do data source devem ser informados. Para isso. indicar o nome do DataDomain (utilizou-se o nome AmanajeDomain para a aplicação Amanaje). há uma dependência que aponta para o driver JDBC do banco de dados utilizado.<groupId>com. para a aplicação Amanaje. é importante que o diretório informado faça parte da sua estrutura. criou-se o AmanajeDataNode.um DataNode corresponde a um data source físico.7</version> <scope>provided</scope> </dependency> <dependency> <groupId>org. em ambientes corporativos – nos quais o nível de complexidade dos sistemas demandam modelos muito mais elaborados – a execução da etapa de ORM seguramente demandaria um diagrama de classes em representação ao modelo de negócio.1-901. Diante disso. No exemplo em questão. Mapeamento ORM com o CayenneModeler De posse do modelo de entidade-relacionamentos da aplicação. como passo inicial será realizada a criação do DataDomain da aplicação – a raiz de todo projeto de mapeamento no CayenneModeler.cayenne</groupId> <artifactId>cayenne-server</artifactId> <version>3. Isso foi feito subsequentemente à criação do DataDomain. dado a simplicidade do escopo envolvido. No contexto do Cayenne. Além disso. porém.sun. No caso da aplicação Amanaje. os objetos serão projetados à medida que o modelo relacional for incorporado à ferramenta gráfica de modelagem. é preciso indicar qual a estratégia de atualização do esquema do banco a ser adotada. em seguida. foi utilizado o caminho $AMANAJE_HOME/src/main/webapp/WEB-INF/cayenne ($AMANAJE_HOME corresponde ao caminho absoluto até o diretório onde o código-fonte da aplicação se encontra).1.faces</groupId> <artifactId>jsf-impl</artifactId> <version>2. dado que esse arquivo será utilizado posteriormente pela aplicação.

jm. o qual se refere ao mapeamento objeto-relacional propriamente dito. o qual disponibiliza uma tela cujos seguintes campos devem ser preenchidos: · DataMap Name: Refere-se ao nome do DataMap criado (para a aplicação em questão.map.jm. Essas classes não vão conter qualquer código. o salvamento das configurações efetuadas acarretará na geração de um terceiro arquivo – AmanajeDomainMap.xml no mesmo diretório indicado para o arquivo cayenne. quando o data source é de responsabilidade da própria aplicação. Nota: A tela de criação de DataNodes do CayenneModeler adicionalmente exige que seja indicada a classe responsável pela instanciação do data source configurado. a classe adequada é a DriverDataSourceFactory (opção default do CayenneModeler e utilizada neste artigo). há a necessidade de criação de um DataMap que agregue os mapeamentos das classes Usuario e TipoQualificacao para as tabelas usuario. Outro conceito já abordado foi o de DataMap. a ferramenta irá gerar as classes correspondentes aos mapeamentos de objetos construídos através da sua interface.amanaje. Além disso. Após o preenchimento dos referidos campos. qualificacao e tipo_qualificacao. foi indicado o pacote br. deve-se selecionar a classe SkipSchemaUpdateStrategy). Observe que a criação desse segundo arquivo na estrutura da aplicação faz-se necessária dado que as informações contidas nele serão usadas posteriormente para se obter acesso aos dados do banco.xml.xml – no mesmo diretório utilizado para criação dos dois arquivos anteriores. · DB Schema: Campo onde deve ser indicado o esquema do banco de dados a ser usado pelo mapeamento (definiu-se um esquema com nome amanaje para o exemplo do artigo). o CayenneModeler adicionará à estrutura da aplicação dois novos pacotes: · br. · DataNode: Campo para seleção do DataNode envolvido no mapeamento. Na realidade. a classe selecionada no campo Schema Update Strategy deve ser CreateIfNoSchemaStrategy (no caso de não permitir que o Cayenne faça atualizações no esquema. Neste pacote da aplicação Amanaje estarão presentes as classes Usuario e TipoQualificacao.model: Nesse pacote. Para o exemplo em desenvolvimento. Após o salvamento das operações (criação e configuração do DataNode e configuração do data source). elas constituirão apenas extensões de outras classes – que também serão geradas pelo CayenneModeler – e que efetivamente conterão a implementação Java do mapeamento objeto-relacional.amanaje. o CayenneModeler irá criar o arquivo AmanajeDomainNode. com base na informação de pacote indicada nesta tela de configuração do DataMap. No caso da aplicação Amanaje. Essa etapa foi efetuada acessando-se o menu Project > Create DataMap (opcionalmente. 13 . Isso é informado no campo DataSource Factory da tela. utilizou-se AmanajeDomainMap).driver. pode-se ter acesso a essa funcionalidade via barra de ferramentas – botão “Create DataMap”). Para aplicações que façam uso de data sources gerenciados por um container e obtidos via chamada JNDI. Por outro lado. a classe (factory) a ser utilizada é a JNDIDataSourceFactory.Logo. · Java Package: Corresponde ao pacote da aplicação Java que conterá as classes geradas pelo CayenneModeler a partir dos mapeamentos construídos.model.

· br. Figura 6. Uma particularidade presente nas classes desse pacote está no fato de todas terem o prefixo “_” no nome.model). A Figura 7 exibe a estrutura da aplicação Amanaje dentro do Eclipse. o pacote conterá as classes _Usuario e _TipoQualificacao. A Figura 6 apresenta a tela do CayenneModeler após a criação das estruturas iniciais do projeto – DataDomain.jm.amanaje.model. Qualquer necessidade de implementação de lógica no modelo de objetos deve ser efetuada nas subclasses presentes no pacote indicado na interface do CayenneModeler (br.jm. 14 .amanaje. DataNode e DataMap – e configuração do AmanajeDomainNode. Todas as classes desse pacote também são geradas automaticamente pela ferramenta e jamais deverão ser alteradas. Tela de configuração do DataNode no CayenneModeler. No caso da aplicação Amanaje.auto: Pacote que conterá as classes que vão implementar o mapeamento objetorelacional propriamente dito.

uma tabela do banco de dados é representada no Cayenne pelo objeto DbEntity. Projeto da aplicação Amanaje. TIPO_QUALIFICACAO) e detalha os atributos do DbEntity usuario. Os valores das propriedades do atributo (Name. Max Length e Scale) são preenchidos em seguida. para a criação da tabela USUARIO. Logo. em conformidade com o diagrama da Figura 5. Mandatory. Para isso. o próximo passo envolve a construção dos mapeamentos objeto-relacionais propriamente ditos. Além disso. Type. iniciou-se o trabalho de mapeamento pela modelagem do esquema do banco. basta acionar o botão Create Attribute. deve-se selecionar o AmanajeDomainMap com o mouse e acionar a opção Create DbEntity (ou pelo menu Project > Create DbEntity). são definidos os seus atributos.Figura 7. PK. localizado na aba Attributes que surge após a seleção do DbEntity recém-criado. Após a atribuição do nome “usuario” ao novo objeto. como já mencionado anteriormente. Dado que a geração do banco de dados foi delegada ao CayenneModeler (como apresentado na configuração do DataNode). Uma vez concluídas as configurações iniciais do projeto no CayenneModeler. Esta sequência de passos deve ser repetida para cada uma das tabelas do banco. QUALIFICACAO. A Figura 8 apresenta a tela do CayenneModeler após a geração dos três DbEntity da aplicação Amanaje (USUARIO. 15 .

a seguinte sequência de passos deve ser executada no CayenneModeler: · Selecione o DbEntity USUARIO e depois a aba Relationships à direita. Logo. o checkbox deve ser desmarcado. Começando com o relacionamento ONE-TO-MANY entre USUARIO e QUALIFICACAO. Para o relacionamento entre USUARIO e QUALIFICACAO foram selecionadas as colunas que contêm as chaves primárias de ambas as tabelas. 16 . se é do DbEntity USUARIO para o DbEntity QUALIFICACAO. Como dito anteriormente e apresentado na Figura 5. recomenda-se que o projeto seja periodicamente salvo. ou vice-versa). · Clique no botão Create Relationship para criar um relacionamento. QUALIFICACAO e TIPO_QUALIFICACAO. Nota: Durante todo o processo de mapeamento com o CayenneModeler. Contudo. · Utilize o botão Add da caixa de diálogo para as colunas usadas na operação de join. embora o relacionamento criado seja do tipo ONE-TO-MANY. ou seja. Este checkbox possibilita a indicação de qual é o sentido TOMANY do relacionamento (neste caso. USUARIO. De posse do modelo de tabelas da aplicação criado no CayenneModeler.id_usuario. · Clique no botão Database Mapping (botão cujo ícone é uma letra “I” dentro de um círculo). Neste ponto. é preciso avançar para a construção dos relacionamentos entre essas tabelas. Tabelas da aplicação Amanaje modeladas no CayenneModeler. porém do DbEntity QUALIFICACAO. Essa ação disponibilizará uma caixa de diálogo para a configuração do relacionamento. a caixa de diálogo do relacionamento USUARIO-QUALIFICACAO deve estar de acordo com a Figura 9. e o renomeie para “qualificacoes” (o nome default gerado pelo CayenneModeler para um relacionamento é “untitledRel”). especialmente porque o Eclipse não consegue identificar as alterações no projeto até que estas tenham sido efetivamente salvas.id e QUALIFICACAO. · Selecione a tabela “Target” QUALIFICACAO. E nessa mesma aba. a caixa de diálogo é fechada e dois relacionamentos complementares são criados: de USUARIO para QUALIFICACAO e vice-versa (no caso de relacionamentos bidirecionais). de volta à aba Relationships. · Após a confirmação das mudanças. o checkbox “To Many” não está marcado. este checkbox deve ser marcado na aba Relationships do DbEntity USUARIO. por se tratar de um relacionamento com apenas um lado MANY.Figura 8. nossa aplicação demanda a criação de relacionamentos entre as tabelas USUARIO. a qual representa o lado MANY do relacionamento.

Figura 9. Configuração do relacionamento USUARIO – QUALIFICACAO. pode-se utilizar o CayenneModeler para gerar as classes Java (ObjEntities) a partir de derivações de cada DbEntity. Assim. Além disso. e com isso. · Clique no botão Sync ObjEntity with DbEntity (botão cujo ícone tem setas amarelas). o seguinte passo-a-passo deve ser executado: · Selecione o DbEntity usuario e clique no botão Create ObjEntity (botão cujo ícone é composto por uma letra “C” dentro um círculo verde). os respectivos relacionamentos não foram adequadamente configurados. Esse mesmo processo deve ser repetido para o DbEntity qualificacao. conforme apresentado pelo esquema da Figura 10. um ObjEntity TipoQualificacao é gerado. · Selecione o DbEntity tipo_qualificacao e clique mais uma vez no botão Create ObjEntity. Uma vez finalizado o mapeamento das duas classes da aplicação. Para essa etapa final. A execução dessa ação deve fazer com que o relacionamento “qualificacoes” seja exibido na aba Relationships. realize a seguinte sequência de passos: · Selecione o ObjEntity Usuario e depois clique na aba Relationships. Para finalizar a etapa de mapeamento. Isso 17 .jm. entre TIPO_QUALIFICACAO e QUALIFICACOES.amanaje. De posse do esquema do banco de dados completamente mapeado.model (pacote que foi definido na criação do AmanajeDomainMap). vale ressaltar o fato de estas serem direcionadas para três tabelas do banco de dados. · Repita esses passos para o ObjEntity TipoQualificacao. Essa ação fará com que um ObjEntity com nome “Usuario” seja criado dentro do DomainMap. a classe Java Usuario é adicionada ao pacote br. Isso se faz necessário porque Usuario e TipoQualificacao foram gerados antes do ObjEntity Qualificacao. Para geração das classes da aplicação Amanaje. é preciso executar uma sincronização dos relacionamentos dos ObjEntities criados. o CayenneModeler transforma os nomes dos atributos do DbEntity usuario em atributos na classe Java correspondente. E após o salvamento do projeto. A mesma sequência de passos descrita deve ser executada para o outro relacionamento ONE-TO-MANY.

getQualificacoes. a partir de um objeto Usuario é possível recuperar instâncias de Qualificacao (e não TipoQualificacao) através deste relacionamento. Figura 10. Note que não há a necessidade de uma invocação tal como usuario. Como já mencionado. Qualificacao e TipoQualificacao possa ser configurado como flattened. então este ObjEntity pode ser removido do projeto.getQualificacoes. no caso da nossa aplicação. a partir da classe Usuario. a terceira tabela (qualificacao) refere-se a uma join table criada para tratar o relacionamento MANY-TO-MANY entre as classes Usuario e TipoQualificacao. Na área referente a Mapping to DbRelationships é preciso configurar a cadeia de relacionamentos que conduz à classe que se deseja retornar (TipoQualificacao) ao acessar essa relação. Contudo. na aba Relationships. ao abrir a tela.porque essa situação traz à tona um conceito implementado pelo Cayenne conhecido como relacionamento flattened. Estrutura do mapeamento criado para a aplicação Amanaje. Observe que. A indicação desse relacionamento como flattened no mapeamento construído via CayenneModeler possibilita que o acesso às instâncias de TipoQualificacao por uma instância de Usuario possa ser feita através de uma chamada direta do tipo usuario. Sendo assim. O uso desse mecanismo possibilita que join tables não precisem ser mapeadas para objetos que não tenham significado algum para o negócio modelado. o DbEntity equivalente precisa ser mantido. Note que o ObjEntity previamente configurado no campo “Target” é o Qualificacao. deve-se executar o seguinte passo-a-passo: 1. apresentando campos para configuração do relacionamento. Isto porque. a tela de diálogo ObjRelationship Inspector será aberta. o DbEntity correspondente deve ser preservado. clique no botão Edit Relationship. Selecione a ObjEntity Usuario e. Com isso. Para que o relacionamento entre as ObjEntities Usuario. Isso significa dizer que. 2. a configuração presente nessa área corresponde ao relacionamento 18 .getTipoQualificacoes(). embora a join table não tenha significado no modelo de objetos (por isso seu respectivo ObjEntity foi removido). Nota: Se um relacionamento flattened envolve uma join table e esta tabela está mapeada para um ObjEntity. ela tem significado no modelo relacional.

“qualificacoes”. e acessar a caixa de diálogo Code Generation (composta pelas abas Code Generator e Classes) por meio do menu Tools > Generate Classes. Na aba Classes é preciso selecionar as classes a serem geradas (como é a primeira vez que será executada essa operação. abrir imagem em nova janela Figura 11. AmanajeDomainMap (ele fica permanentemente visível na árvore do projeto. todas devem ser selecionadas). exibida na tela principal da ferramenta). isto é. e de posse do projeto da aplicação preparado no Eclipse. Cada clique irá exibir o próximo relacionamento disponível. Caixa de diálogo para configuração do relacionamento flattened. este deve ser um diretório (pacote) da aplicação em desenvolvimento. o qual dá acesso a instâncias da classe Qualificacao (classe esta que representa a join table). Logo. basta selecionar o DomainMap criado. 19 . é necessário modificar o relacionamento na área Mapping to DbRelationships. Naturalmente. Neste exemplo. Para a geração das classes a partir do modelo de ObjEntities construído. A criação efetiva das classes acontece após o acionamento do botão Generate da caixa de diálogo aberta. Isso é feito simplesmente clicando-se com o mouse nas áreas em branco disponíveis nessa seção da tela. Geração de classes e esquema de banco Uma vez finalizado o trabalho de mapeamento objeto-relacional. para que seja possível retornar objetos do tipo TipoQualificacao. foi necessária a adição de apenas mais uma relação – “tipo” –. 3. a qual dá acesso à classe TipoQualificacao. Na aba Code Generator deve ser informado o Output Directory correspondente ao diretório no qual as classes serão geradas. é possível proceder para a criação das classes Java e do esquema do banco de dados. Clique no botão Select Path e confirme com o botão Done. A Figura 11 apresenta o estado final da caixa de diálogo após a configuração do relacionamento flattened do ObjEntity Usuario.

com/xml/ns/javaee http://java. Essa mesma necessidade se fez presente para a integração do Cayenne. que dispara a execução do script para geração do esquema do banco de dados.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param> <filter> <filter-name>CayenneFilter</filter-name> <filter-class>org.conf.faces. foi necessária a inclusão de alguns parâmetros no arquivo web.xml.xml da aplicação Amanaje. Se o data source para o banco da aplicação já tiver sido criado.xml.Para geração do esquema do banco dados. Construindo a aplicação Amanaje Neste ponto do processo de desenvolvimento da aplicação Amanje. Após o acionamento do botão Continue. que indica as tabelas que serão geradas a partir dos DbEntities modelados no CayenneModeler.path</param-name> <param-value>/WEB-INF/cayenne</param-value> </context-param> <context-param> <param-name>javax. e o botão Generate. <?xml version="1. · Geração do banco de dados. serão disponibilizadas na mesma caixa de diálogo duas abas: SQL Options.sun.apache.xsd" xsi:schemaLocation="http://java. então basta referenciá-lo no campo Saved DataSources.faces.org/2001/XMLSchema-instance" xmlns="http://java. que possibilita que o script seja salvo.cayenne.sun. Caso contrário.xsd" id="WebApp_ID" version="2.WebApplicationContextFilter</filter-class> </filter> <filter-mapping> <filter-name>CayenneFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax. · Mapeamento objeto-relacional das classes de negócio para o esquema do banco. os dados para a conexão devem ser informados nos campos disponíveis.sun. A Listagem 3 apresenta essas configurações incluídas no web.com/xml/ns/javaee" xmlns:web="http://java. Arquivo web.webapp.configuration.sun.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www. é possível dar seguimento à implementação das camadas de visão e de persistência da aplicação. Logo.com/xml/ns/javaee/web-app_2_5. Note que duas opções são apresentadas nessa caixa de diálogo: o botão Save SQL. acesse a opção de menu Tools > Generate Database Schema.com/xml/ns/javaee/web-app_2_5. contendo o script SQL a ser executado no banco informado.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> 20 .w3. Camada de visão A implementação da interface com o usuário foi feita com base na tecnologia JavaServer Faces.5"> <display-name>amanaje</display-name> <context-param> <param-name>cayenne. já estão finalizadas as seguintes etapas: · Geração das classes de negócio. Listagem 3. Para a utilização do JSF no projeto. e Tables.

cayenne. · Indicar o filtro (neste caso. deve ser definido um mapeamento para que todas as requisições da aplicação passem por esse filtro.xhtml – é importante observar que esta referencia outros quatro arquivos XHTML.</servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*. Naturalmente. Isso é feito através do parâmetro cayenne. estas farão uso de diferentes DataContext e.xml. <?xml version="1.path. Uma vez finalizadas as configurações iniciais no web. Este será responsável por tratar todas requisições a páginas que detenham a extensão .xhtml. o qual deve referenciar o diretório no qual o arquivo cayenne.configuration.dtd"> <html xmlns="http://www. Além disso. Nota: Numa aplicação web que faz uso do Cayenne.css"/> <h:outputScript name="js/crud.org/TR/xhtml1/DTD/xhtml1-transitional. pode-se dar início à construção da interface web propriamente dita. os quais constituem arquivos de composição da página. CayenneFilter) responsável pela associação do DataContext às requisições do usuário e que referencie a classe org.webapp.WebApplicationContextFilter. A Listagem 4 apresenta o código do arquivo crud.xhtml.xhtml</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.faces.conf.js"/> </h:head> <h:body> <h:outputText value="Gerenciamento de Usuários" class="header"/> <br/> <br/> <fieldset> <legend>Painel de Controle</legend> <h:panelGrid columns="2"> <h:commandButton value="Criar Usuário" onclick="showdiv('div_cadastro')"/> <h:commandButton value="Listar Usuários" onclick="showdiv('div_listagem')"/> 21 . no caso de existirem diferentes sessões.w3.apache.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/facelets"> <h:head> <title>Amanajé</title> <h:outputStylesheet name="css/crud.html</welcome-file> </welcome-file-list> </web-app> As configurações demandadas pelo JSF estão associadas ao servlet nomeado de Faces Servlet.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.xml se encontra.w3. Já para a integração do Cayenne com a aplicação web.xml: · Indicar o arquivo de configurações do Cayenne a ser carregado no contexto da aplicação quando esta é iniciada.FacesServlet. Código da página crud.xhtml.com/jsf/html" xmlns:ui="http://java. Embora todo o código da view esteja centralizado em uma única página XHTML – crud. é necessário o acréscimo de dois parâmetros ao web.sun. de conjuntos diferentes de objetos.0 Transitional//EN" "http://www. portanto. uma mesma instância de DataContext é compartilhada por toda a sessão do usuário. Listagem 4. o qual referencia a classe javax.

exibirMensagemSucesso}"/> <h:outputText value="Nenhum usuário rendered="#{usuarioBean.xhtml – responsável pela captação dos dados para inserção de um novo usuário.xhtml).xhtml) que compõem a página crud. neste arquivo da aplicação. 3. a decisão de exibição das mensagens de sucesso ou falha após a execução das operações. 22 . Criar usuário com suas respectivas qualificações (arquivo criar. A fim de manter o foco na utilização do Cayenne.xhtml.xhtml"/> <ui:include src="listar. Atualizar um usuário (arquivo atualizar. 4. atualizar. Neste painel.xhtml"/> </fieldset> <h:messages/> <div id="msg_sucesso"> <h:outputText value="Operação Efetuada rendered="#{usuarioBean.xhtml"/> <ui:include src="atualizar. foram implementadas quatro operações básicas para a manipulação dos dados: 1. Naturalmente. A visibilidade de cada formulário é controlada por código JavaScript através do método showdiv. 2.exibirMensagemFalhaBusca}"/> </div> com foi Sucesso!" encontrado!" </h:body> </html> Note que. Outro detalhe da página da aplicação é o uso de um único Managed Bean – referenciado como usuarioBean – no qual todo o código de controle da view está implementado. Essas operações podem ser acionadas pelo Painel de Controle da aplicação.xhtml contêm código XHTML análogo ao que foi implementado para a funcionalidade de criação de usuário. o qual é disparado a partir do evento onClick associado a cada botão do Painel de Controle.xhtml e remover. Listar os usuários e qualificações cadastrados no sistema (arquivo listar. os demais arquivos (listar.xhtml).<h:commandButton value="Atualizar Usuário" onclick="showdiv('div_atualizacao')"/> <h:commandButton value="Remover Usuário" onclick="showdiv('div_remocao')"/> </h:panelGrid> </fieldset> <br/> <br/> <fieldset> <legend>Painel de Operações</legend> <ui:include src="criar. Remover um usuário e suas qualificações do sistema (arquivo remover. cada botão disponibiliza um formulário no Painel de Operações para execução da respectiva operação.xhtml).xhtml"/> <ui:include src="remover. como por exemplo.xhtml). será apresentado o código completo de apenas um arquivo de composição da página – criar.

org/TR/xhtml1/DTD/xhtml1-transitional."/> <h:outputLabel value="Data de Nascimento (dd/mm/aaaa)" for="dtNascimento_usuario" /> <h:inputText value="#{usuarioBean.xhtml. Código do arquivo criar.org/1999/xhtml" xmlns:h="http://java. private List<Usuario> usuariosCadastrados.xhtml e a Listagem 6 exibe o código do Managed Bean associado à página crud.tiposQualificacoes}"/> </h:selectManyCheckbox> <h:commandButton action="#{usuarioBean.com/jsf/html" xmlns:f="http://java."> <f:selectItem itemLabel="Masculino" itemValue="M"/> <f:selectItem itemLabel="Feminino" itemValue="F"/> </h:selectOneRadio> <h:outputLabel value="Qualificação" for="curso_usuario"/> <h:selectManyCheckbox value="${usuarioBean.sun."> <f:convertDateTime pattern="dd/MM/yyyy"/> </h:inputText> <h:outputLabel value="Sexo" for="sexo_usuario"/> <h:selectOneRadio value="#{usuarioBean.dataNascimento}" id="dtNascimento_usuario" maxlength="10" size="10" required="true" requiredMessage="Data de nascimento obrigatória.usuario. @ManagedBean @RequestScoped public class UsuarioBean { private boolean exibirMensagemSucesso. private boolean exibirMensagemFalhaBusca. private Usuario usuario."/> <h:outputLabel value="Nome do Usuário" for="nome_usuario" /> <h:inputText value="#{usuarioBean.sun.usuario.w3. Listagem 5. 23 .sexo}" id="sexo_usuario" required="true" requiredMessage="Campo sexo obrigatório.com/jsf/facelets"> <ui:composition> <div id="div_cadastro" class="div_operacao"> <div id="titulo_operacao"> <h:outputText value="Cadastro de Usuário"/> </div> <h:form> <h:panelGrid columns="2"> <h:outputLabel value="Documento" for="doc_usuario" /> <h:inputText value="#{usuarioBean.A Listagem 5 apresenta o código do arquivo criar. Código do managed bean UsuarioBean.xhtml.w3.usuario.dtd"> <html xmlns="http://www. <?xml version="1.nivelQualificacaoes}" id="curso_usuario"> <f:selectItems value="#{usuarioBean.identidade}" id="doc_usuario" maxlength="30" size="30" required="true" requiredMessage="Documento de identificação do usuário obrigatório.nome}" id="nome_usuario" maxlength="60" size="60" required="true" requiredMessage="Nome do usuário obrigatório.usuario.0 Transitional//EN" "http://www.sun.cadastrar}" value="Cadastar Usuário" /> </h:panelGrid> </h:form> </div> </ui:composition> </html> Listagem 6.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.com/jsf/core" xmlns:ui="http://java.

exibirMensagemSucesso = this.add(new SelectItem(tipo. } else { this. tipo.exibirMensagemSucesso = true.criar(usuario. limparContexto(). public UsuarioBean() { this.usuario == null) { this.tipoDao = new TipoQualificacaoDAO(). this. private int[] nivelQualificacaoes. if (this.tipoDao.atualizar(usuario. 24 .exibirMensagemSucesso = true. } else { this.exibirMensagemFalhaBusca = true. private TipoQualificacaoDAO tipoDao. } gerarNiveisQualificacoesUsuario().getNivel().usuario.usuario = new Usuario().exibirMensagemFalhaBusca = true.usuariosCadastrados = new ArrayList<Usuario>(). } else { this. this. loadTiposQualificacoes().private SelectItem[] tiposQualificacoes. } public void recuperar() { if (this. nivelQualificacaoes).usuarioDao.usuarioDao. } } private void carregarTiposQualificacoes() { List<SelectItem> listaItens = new ArrayList<SelectItem>(). this.getIdentidade()). return.usuarioDao.remover(usuario)) { this.nivelQualificacaoes = null.usuariosCadastrados = this.usuarioDao = new UsuarioDAO(). } public void cadastrar() { this.usuarioDao.recuperar(usuario. this.usuario = this. this.recuperarLista(). } public void limparContexto() { this. limparContexto().usuarioDao. limparContexto(). for (TipoQualificacao tipo : this. private UsuarioDAO usuarioDao. } } public void atualizar() { if (this. this.exibirMensagemFalhaBusca = false.cadastrarListaPadrao()) listaItens.exibirMensagemFalhaBusca = true.getCurso())). } } public void remover() { if (this.temIdentidade()) { this.exibirMensagemSucesso = true. nivelQualificacaoes)) { limparContexto().

Isso possibilita que. private TipoQualificacaoDAO tipoDao. · gerarNiveisQualificacoesUsuario(): Extrai os códigos dos tipos de qualificações recuperados de um usuário.commitChanges().getQualificacoes().registerNewObject(usuario). atualizar() e remover().getQualificacoes() == null || this. this. Listagem 7. Todo o código de interação com o Cayenne está centralizado nessas duas classes.context. Ainda no managed bean. public UsuarioDAO() { this. for (int numCurso = 0.tipoDao = new TipoQualificacaoDAO(context).isEmpty()) return. int[] niveisQualificacoes) { this.usuario. os quais executam as funcionalidades oferecidas pelo sistema. } public void criar(Usuario usuario. As Listagens 7 e 8 apresentam o código das classes de persistência responsáveis pela interação com o Cayenne. this.getQualificacoes().get(numCurso++). foram implementados essencialmente quatro métodos: cadastrar().nivelQualificacaoes = new int[quantQualificacoes]. numCurso < quantQualificacoes.tipoDao. e os armazena em um atributo do bean (nivelQualificacoes). por ocasião de uma consulta a um usuário.usuario. Os tipos disponíveis são armazenados num atributo do bean (tiposQualificacoes) que está associado a um componente da tela. Código da classe UsuarioDAO.nivelQualificacaoes[numCurso] = this. seus tipos de qualificação associados possam ser utilizados por um componente de tela na renderização da página.addToQualificacoes(tipo). this.tiposQualificacoes = listaItens.context. } this. dois métodos adicionais e privados foram construídos para dar suporte aos métodos principais: · carregarTiposQualificacoes(): Registra na base de dados e recupera dela os tipos de qualificações disponíveis para serem associados aos usuários do sistema.toArray(new SelectItem[listaItens. } /** * Getters e Setters */ } Observe que ao UsuarioBean estão agregados dois objetos que disponibilizam o acesso aos dados do sistema – UsuarioDAO e TipoQualificacaoDAO. usuario.usuario. for (int nivel : niveisQualificacoes) { TipoQualificacao tipo = this.usuario. Já a Listagem 9 apresenta o código de um Enum auxiliar.getQualificacoes(). usado no cadastro dos tipos de qualificações disponibilizados pelo sistema. } 25 . recuperar().this.recuperarPorNivel(nivel). } private void gerarNiveisQualificacoesUsuario() { if (this.getThreadObjectContext().size(). public class UsuarioDAO { private ObjectContext context. int quantQualificacoes = this. Além destes.context = BaseContext.size()]).getNivel()).

List<Usuario> listaRecuperada = this.getQualificacoes()). public TipoQualificacaoDAO() { this.merge(usuario). listaCursos. for (TipoQualificacao curso : listaCursos) usuarioCommited.removeFromQualificacoes(curso).ASCENDING).getIdentidade()). this.getThreadObjectContext(). } public boolean remover(Usuario usuario) { Usuario usuarioCommited = recuperar(usuario.context.context.context. SortOrder. List<TipoQualificacao> listaCursos = new ArrayList<TipoQualificacao>().IDENTIDADE_PROPERTY. usuarioCommited. if (usuarioCommited == null) return false. return this.removeFromQualificacoes(curso). listaCursos.deleteObject(usuarioCommited). } @SuppressWarnings("unchecked") public List<Usuario> recuperarLista() { SelectQuery query = new SelectQuery(Usuario. if (usuarioCommited == null) return false.performQuery(query). } 26 .commitChanges().matchExp(Usuario. query.context.context = BaseContext.class).addOrdering(Usuario.context = context. for (TipoQualificacao curso : listaCursos) usuarioCommited.isEmpty()) ? (Usuario) listaRecuperada.tipoDao.addAll(usuarioCommited. } @SuppressWarnings("unchecked") public Usuario recuperar(String identidade) { SelectQuery query = new SelectQuery(Usuario. identidade)).NOME_PROPERTY.public boolean atualizar(Usuario usuario.context. ExpressionFactory.class. public class TipoQualificacaoDAO { private ObjectContext context.get(0) : null.getIdentidade()). List<TipoQualificacao> listaCursos = new ArrayList<TipoQualificacao>().addAll(usuarioCommited.commitChanges(). this. this. } public TipoQualificacaoDAO(ObjectContext context) { this. for (int nivel : niveisQualificacoes) usuarioCommited.addToQualificacoes(this. Código da classe TipoQualificacaoDAO. return true.recuperarPorNivel(nivel)). int[] niveisQualificacoes) { Usuario usuarioCommited = recuperar(usuario.performQuery(query).getQualificacoes()). } } Listagem 8. return listaRecuperada != null && (! listaRecuperada. return true.

} this.NIVEL_PROPERTY. GRADUACAO("Graduação". DOUTORADO("Doutorado".getCurso()). "DTD"). if (listaTipos.NIVEL_PROPERTY.isEmpty()) { for (Qualificacao qualificacao : Qualificacao.context.performQuery(query).setSigla(qualificacao.addOrdering(TipoQualificacao. tipo. 40. SortOrder.setNivel(qualificacao. } } Listagem 9. ExpressionFactory. return listaRecuperada != null && (! listaRecuperada.curso = curso. 10. 50.context. } @SuppressWarnings("unchecked") public TipoQualificacao recuperarPorNivel(int nivel) { SelectQuery query = new SelectQuery(TipoQualificacao. } public int getNivel() { return nivel. MESTRADO("Mestrado". "ESP"). Código do enum Qualificacao.ASCENDING). "MST"). int nivel.context. "ENM"). List<TipoQualificacao> listaRecuperada = this.context. nivel)). public enum Qualificacao { ENSINO_MEDIO("Ensino Médio". } 27 . 30.registerNewObject(tipo). query. this. this. return this.isEmpty()) ? (TipoQualificacao) listaRecuperada. private String sigla. } return listaTipos. private int nivel.class. tipo.public List<TipoQualificacao> cadastrarListaPadrao() { List<TipoQualificacao> listaTipos = recuperarLista().getSigla()). } @SuppressWarnings("unchecked") public List<TipoQualificacao> recuperarLista() { SelectQuery query = new SelectQuery(TipoQualificacao.get(0) : null.sigla = sigla.setCurso(qualificacao. private String curso.matchExp(TipoQualificacao. this. } public String getSigla() { return sigla.nivel = nivel. private Qualificacao(String curso. ESPECIALIZACAO("Especialização".class). 20. } public String getCurso() { return curso.getNivel()).commitChanges(). String sigla) { this.values()) { TipoQualificacao tipo = new TipoQualificacao(). "GRD").performQuery(query). tipo.

via método commitChanges(). deve-se executar uma chamada ao método commitChanges() do contexto de persistência. e um array contendo as novas qualificações que devem substituir as atualmente persistidas. Isso faz com que o objeto deixe o estado new e passe para o estado commited. Após o commit das modificações. todas as correspondentes qualificações recuperadas são removidas e. primeiramente é necessário registrar o objeto. Uma vez construída a consulta. Para informar os parâmetros da consulta. Note que o método recebe como parâmetro um usuário com seus atributos atualizados. nenhuma modificação efetuada nos objetos foi refletida nos respectivos registros do banco de dados. Essa referência é recuperada a partir de uma chamada ao método estático getThreadObjectContext() da classe abstrata BaseContext. novamente o método commitChanges() do contexto de persistência é invocado. a qual abstrai a cláusula where do código SQL. então é preciso recuperar essa lista e agregá-la à nova instância de Usuario. que foi buscada da base. No caso do método recuperar(). o respectivo registro do banco de dados é removido. Como o método criar() não recebe a lista de TipoQualificacao do usuário. · recuperar() e recuperarLista(): Em ambos os métodos. o qual faz com que a instância deixe o estado transient e passe ao estado new. esta é executada via chamada performQuery() do contexto de persistência.matchExp(). De posse dessa instância. é utilizada a chamada ExpressioinFactoy. Para que isso seja executado. Porém. Isso é feito via chamada ao método registerNewObject(). a recuperação dos objetos é feita por meio da classe SelectQuery. mas sim um array com os códigos das respectivas qualificações. Logo.} Ao analisar o código de persistência. ela também pode ser passada como parâmetro para o construtor da classe. no caso de TipoQualificacaoDAO. · atualizar(): Executa a atualização de usuários no sistema. até este ponto. a instância recuperada do banco tem seus atributos sobrescritos pela instância passada como parâmetro. o novo usuário está pronto para ser persistido como registro no banco de dados. E. Avançando ao detalhamento de cada DAO. Para isso. juntamente com suas respectivas qualificações. Esta operação acarreta ainda que a instância de Usuario. A invocação do método deleteObject() faz com que o objeto saia do estado commited e passe para o estado deleted. observe que a classe UsuarioDAO implementa cinco métodos para manipulação dos dados via Cayenne. substituídas pelas pertencentes ao array informado. primeiramente é preciso buscar a instância de Usuario que está no banco. é importante notar que os métodos construtores de ambas as classes buscam a referência ao ObjectContext para poderem interagir com o Cayenne. passado como parâmetro. São eles: · criar(): Para inserção de um novo usuário no sistema. Além disso. Uma vez executadas todas essas operações. salvo pelo fato de que as qualificações agregadas à instância informada serão apenas removidas e não substituídas. um parâmetro referente à identidade do usuário deve ser informado. e então o objeto retorna para o estado commited. deixe o estado commited e passe para o estado modified. 28 . · remover(): A remoção do objeto Usuario e suas respectivas qualificações se dá de forma análoga à atualização. no contexto de persistência (ObjectContext). subsequentemente.

[org. using automatic adapter.conf.apache. [org.apache. [org.cayenne. [org. connections in the pool: 3 [org.apache.RuntimeLoadDelegate] started configuration loading.apache.DriverDataSourceFactory [org.web. Por sua vez.conf.conf.DriverDataSourceFactory' schema-updatestrategy='org.cayenne.service.webcontainer.access.RuntimeLoadDelegate] loading <node name='AmanajeDomainNode' datasource='AmanajeDomainNode. · Conexão com o banco de dados e geração do pool de conexões.RuntimeLoadDelegate] using factory: org. [org.config] Monitoring jndi:/localhost/amanaje/WEB-INF/faces-config.driver.jboss.Driver Min.conf.postgresql.cayenne.RuntimeLoadDelegate] loaded <map name='AmanajeDomainMap' location='AmanajeDomainMap. connections in the pool: 1 Max. · Carregamento das configurações do projeto Cayenne (AmanajeDomain) presentes no arquivo cayenne.enterprise.resource.apache. [org.cayenne. ctxPath=/amanaje [javax.RuntimeLoadDelegate] no adapter set.cayenne.map.apache. [org. [org. A sua instalação no container JBoss AS gera um log cuja análise permite verificar a sequência de criação e instanciação dos diversos recursos por ela utilizados.jsf.DriverDataSourceFactory] loading driver information from 'AmanajeDomainNode. vários recursos são acionados: · Carregamento da implementação JSF do JBoss (Mojarra).apache.xml for modifications [org.apache.conf.QueryLogger] Created connection pool: jdbc:postgresql://localhost:5432/postgres Driver class: org.enterprise. por ocasião do carregamento do contexto da aplicação (/amanaje) no container. Um trecho desse log é apresentado na Listagem 10. · Carregamento das configurações de acesso ao banco de dados – AmanajeDomainNode.3 ( b05) para o contexto '/amanaje' [javax.cayenne.cayenne.apache.conf.DriverDataSourceFactory] loading driver org. Pode-se observar que. · Carregamento dos mapeamentos objeto-relacionais construídos (AmanajeDomainMap) e indicados no arquivo AmanajeDominMap.deployers. 29 .dbsync.cayenne.conf.conf.A segunda classe de persistência construída – a classe TipoQualificacaoDAO – implementa apenas dois métodos principais – recuperarLista() e recuperarporNivel() – e um método de apoio – cadastrarListaPadrao().driver.RuntimeLoadDelegate] loaded datasource.apache.RuntimeLoadDelegate] loaded map-ref: AmanajeDomainMap.jsf.xml'.cayenne.postgresql. Finalizada a construção da camada de persistência.conf.xml'>.DriverDataSourceFactory] loading user name and password. [org.apache.resource.apache.conf.xml. Este último é usado apenas para cadastrar os tipos de qualificação do sistema na base de dados.cayenne.TomcatDeployment] deploy.driver. a implementação dos dois primeiros métodos é análoga ao que foi construído na classe UsuarioDAO.Driver [org.cayenne. Listagem 10.xml' factory='org.cayenne.xml.cayenne.RuntimeLoadDelegate] finished configuration loading in 132 ms.apache.map. Log de inicialização da aplicação Amanaje no JBoss AS.RuntimeLoadDelegate] loaded domain: AmanajeDomain [org.cayenne. a aplicação Amanaje encontra-se pronta para deploy.config] Inicializando Mojarra 2.cayenne.conf.conf.conf.access.xml.0.tomcat.cayenne.apache.apache.apache.CreateIfNoSchemaStrategy'>.webcontainer.conf.

sigla.apache.QueryLogger] --.access.nivel = ? [bind: 1->nivel:30] [org. ?.QueryLogger] +++ transaction committed.cayenne. Na primeira transação.cayenne. id.apache.access.apache. sexo) VALUES (?. identidade. Já a Listagem 11 apresenta um trecho do log gerado após o acionamento do botão Cadastrar Usuário da aplicação. são recuperados do banco todos os tipos de qualificação a serem disponibilizados num atributo do bean e associados a um componente da tela. Note que o log indica a execução de duas transações. nome.QueryLogger] --.cayenne.apache.QueryLogger] INSERT INTO amanaje.tipo_qualificacao t0 WHERE t0.cayenne.access.apache. 2->id:280.access. [org.apache. [org. 4->nome:'Java Magazine DevMedia'.access.cayenne.apache.transaction started.QueryLogger] --.will run 1 query.QueryLogger] SELECT nextval('amanaje. t0. [org.apache.apache. 2>id_usuario:280] [org.pk_usuario') [org. ambas delimitadas pelas informações de transaction started e transaction commited: 1.cayenne.access. ?) [org.access.qualificacao (id_tipo.access.took 1 ms.apache.id. [org. [org.QueryLogger] [batch bind: 1->data_nascimento:'2014-04-13 21:00:00.cayenne.cayenne.apache. Log gerado para o cadastramento de um usuário na aplicação.cayenne. [org.usuario (data_nascimento.QueryLogger] --. Listagem 11. 3->identidade:'123456'.QueryLogger] [batch bind: 1->id_tipo:221. [org.will run 2 queries.apache.access. t0.access.access.QueryLogger] === updated 1 row.access.access. Tela de cadastro da aplicação Amanaje. . ?.nivel FROM amanaje.cayenne.QueryLogger] SELECT t0.cayenne. 30 .apache.QueryLogger] +++ transaction committed. id_usuario) VALUES (?.A Figura 12 apresenta uma visão da interface principal da aplicação Amanaje juntamente com o formulário de cadastro de usuário preenchido.0'.cayenne. 5->sexo:'M'] [org.QueryLogger] === returned 1 row. Figura 12.transaction started.QueryLogger] === updated 1 row. ?) [org. [org.apache. [org.access.QueryLogger] INSERT INTO amanaje.cayenne. t0.apache. ?.cayenne.cayenne.access.curso.

por exemplo. A utilização do Cayenne constitui uma alternativa simples e eficiente para aplicações cuja persistência é baseada em mapeamentos objeto-relacionais. c) Um novo registro é criado na join table QUALIFICACAO para indicar os tipos de qualificações cadastrados para o usuário informado. b) Um novo registro.195. isso possibilitou a manutenção do foco na utilização do Cayenne e na apresentação dos seus recursos. ressalta-se novamente que diversos aspectos considerados como boas práticas para a construção de aplicações web foram abstraídos. contendo os dados informados na tela. Contudo.org/ Artigo que apresenta o passo-a-passo de criação de um projeto com o Apache Cayenne. Contudo.com/[/nota] 31 .com/news/1364760/Cayenne-BeingProductive-with-Object-Relational-Mapping Fórum de discussão sobre o Apache Cayenne. através de sua integração com uma poderosa ferramenta gráfica – o CayenneModeler.n3. a saber: a) Um novo valor é gerado para representar a primary key do registro a ser inserido na tabela USUARIO. ele agrega o diferencial de abstrair do desenvolvedor qualquer configuração via anotações ou arquivos XML. Assim como a vasta gama de frameworks que implementam a especificação JPA. Links Site oficial do projeto Apache Cayenne.apache. Na segunda transação.2. é inserido na tabela USUARIO. Isso confere ao Cayenne o caráter de uma ferramenta ágil e adequada para a implementação de projetos que façam uso de modelos orientados a objetos persistidos em bancos de dados relacionais.nabble. Neste ponto. ocorre a manipulação dos dados do banco envolvidos na efetivação do cadastro do usuário. Esse é caso. http://cayenne. https://cayenne. http://www. ele oferece um mecanismo de ORM que contempla requisitos para soluções nos mais diferentes contextos.theserverside. da internacionalização de Strings e validação de dados.