Introdução ao Apache Cayenne ORM

Introdução ao Apache Cayenne ORM

1. Visão geral

Apache Cayenne é uma biblioteca de código aberto, distribuída sob a licença Apache, fornecendo recursos como uma ferramenta de modelagem, mapeamento objeto-relacional também conhecido como ORM para operações de persistência local e serviços remotos.

Nas seções a seguir, veremos como interagir com um banco de dados MySQL usando o Apache Cayenne ORM.

2. Dependências do Maven

Para começar, precisamos apenas adicionar as seguintes dependências para ativar o conector Apache Cayenne e MySQL e o driver JDBC para acessar nosso banco de dadosintro_cayenne:


    org.apache.cayenne
    cayenne-server
    4.0.M5


    mysql
    mysql-connector-java
    5.1.44
    runtime

Vamos configurar o plugin do modelador Cayenne que será usado para projetar ou definir nosso arquivo de mapeamento que atua como uma ponte entre o esquema do banco de dados e o objeto Java:


    org.apache.cayenne.plugins
    maven-cayenne-modeler-plugin
    4.0.M5

Em vez de construir o arquivo de mapeamento XML manualmente (feito raramente), é recomendado usar o modelador, que é uma ferramenta bastante avançada que vem com a distribuição Cayenne.

Ele está disponível para download nestearchive dependendo do seu sistema operacional ou apenas use a versão de plataforma cruzada (JAR) incluída como um plugin Maven lá.

O repositório Maven Central hospeda as versões mais recentes deApache Cayenne,his modeler eMySQL Connector.

A seguir, vamos construir nosso projeto commvn installe lançar a GUI do modelador com o comandomvn cayenne-modeler:run para obter como saída esta tela:

image

3. Configuração

Para fazer o Apache Cayenne procurar o banco de dados local correto, só precisamos preencher seu arquivo de configuração com o driver correto, URL e um usuário no arquivocayenne-project.xml localizado no diretórioresources:



    
        
            
            
            
            
        
    

Aqui podemos ver que:

  • O banco de dados local é denominadointro_cayenne

  • Se ainda não foi criado, Cayenne fará isso por nós

  • Faremos a conexão usando o nome de usuárioroote a senharoot (altere de acordo com os usuários registrados em seu sistema de gerenciamento de banco de dados)

Internamente, é oXMLPoolingDataSourceFactory responsável por carregar as informações de conexão JDBC de um recurso XML associado aoDataNodeDescriptor.

Esteja ciente de que esses parâmetros são relativos ao sistema de gerenciamento de banco de dados e a um driver JDBC, porque esta biblioteca pode suportar muitos bancos de dados diferentes.

Cada um deles tem um adaptador disponível nestelist detalhado. Observe que a documentação completa da versão 4.0 ainda não está disponível, portanto, nos referimos à versão anterior aqui.

4. Mapeamento e design de banco de dados

4.1. Modelagem

Vamos agora clicar em“Open Project”, navegar até a pasta de recursos do projeto e escolher o arquivocayenne-project.xml, que o modelador mostrará:

image

Aqui,we’ve got the choice to either create our mapping structure from an existing databaseor to proceed manually. Este artigo tratará daquele que usa o modelador e o banco de dados existente para entrar em Cayenne e saber rapidamente como funciona.

Vamos dar uma olhada em nosso banco de dadosintro_cayenne, que tem um relacionamentoone-to-many em duas tabelas, já que um autor pode publicar ou possuir muitos artigos:

  • author: id (PK) ename

  • article: id (PK), title, content, eauthor_id(FK)

Agora vamos para “Tools > Reengineer Database Schema“, e teremos todas as nossas configurações de mapeamento preenchidas automaticamente. Na tela de prompt, basta preencher a configuração da fonte de dados disponível lá no arquivocayenne-project.xmle clicar em continuar:

image

Na próxima tela, precisamos verificar “Usar tipos primitivos Java” da seguinte maneira:

image

Precisamos também nos certificar de colocarcom.example.apachecayenne.persistent como pacote Java e salvá-lo; veremos que o arquivo de configuração XML foi atualizado para sua propriedadedefaultPackage para corresponder ao pacote Java:

image

Em cadaObjEntity, devemos especificar o pacote para as subclasses conforme mostrado na imagem a seguir e clicar no ícone“save” novamente:

image

Agora no menu“Tools > Generate Classes”, selecione “Standard Persistent Objects” como o tipo; e na guia“Classes” verifique todas as classes e pressione“generate”.

Vamos voltar ao código-fonte para ver se nossos objetos persistentes foram gerados com sucesso, falando sobreArticle.java_ and Author.java_.

Observe que todas essas configurações são salvas no arquivodatamap.map.xml também localizado na pastaresources.

4.2. Estrutura de mapeamento

O arquivo de mapeamento XML gerado apresentado na pasta do recurso está usando algumas tags exclusivas relativas ao Apache Cayenne:

  • DataNode(<node>) - o modelo do banco de dados, seu conteúdo todas as informações necessárias para se conectar a um banco de dados (o nome do banco de dados, o driver e as credenciais do usuário)

  • DataMap(<data-map>) - é um contêiner de entidades persistentes com suas relações

  • DbAttribute(<db-attribute>) - representa uma coluna em uma tabela de banco de dados

  • DbEntity(<db-entity>) - o modelo de uma única tabela ou visão do banco de dados, pode ter DbAttributes e relacionamentos

  • ObjEntity(<obj-entity>) - o modelo de uma única classe java persistente; feito de ObjAttributes que correspondem às propriedades da classe de entidade e ObjRelationships que são propriedades que têm um tipo de outra entidade

  • Embeddable(<embeddable>) - o modelo de uma classe Java que atua como uma propriedade de uma ObjEntity, mas corresponde a várias colunas no banco de dados

  • Procedure(<procedure>) - para registrar o procedimento armazenado no banco de dados

  • Query(<query>) - o modelo de uma consulta, usado para mapear a consulta no arquivo de configuração sem esquecer que também podemos fazer isso no código

Aqui estão osdetails completos.

5. API Cayenne

A única etapa restante é usar a API Cayenne para executar nossas operações de banco de dados usando classes geradas, sabendo que a subclasse de nossas classes persistentes é apenas uma prática recomendada para personalizar o modelo posteriormente.

5.1. Criando um objeto

Aqui, basta salvar um objetoAuthor e verificar posteriormente se existe apenas um registro desse tipo no banco de dados:

@Test
public void whenInsert_thenWeGetOneRecordInTheDatabase() {
    Author author = context.newObject(Author.class);
    author.setName("Paul");

    context.commitChanges();

    long records = ObjectSelect.dataRowQuery(Author.class)
      .selectCount(context);

    assertEquals(1, records);
}

5.2. Lendo um objeto

Depois de salvar umAuthor, apenas o escolhemos entre outros por meio de uma consulta simples por uma propriedade particular:

@Test
public void whenInsert_andQueryByFirstName_thenWeGetTheAuthor() {
    Author author = context.newObject(Author.class);
    author.setName("Paul");

    context.commitChanges();

    Author expectedAuthor = ObjectSelect.query(Author.class)
      .where(Author.NAME.eq("Paul"))
      .selectOne(context);

    assertEquals("Paul", expectedAuthor.getName());
}

5.3. Recuperando todos os registros de uma classe

Salvaremos dois autores e recuperaremos uma coleção de objetos de autor para verificar se há apenas estes dois salvos:

@Test
public void whenInsert_andQueryAll_thenWeGetTwoAuthors() {
    Author firstAuthor = context.newObject(Author.class);
    firstAuthor.setName("Paul");

    Author secondAuthor = context.newObject(Author.class);
    secondAuthor.setName("Ludovic");

    context.commitChanges();

    List authors = ObjectSelect
      .query(Author.class)
      .select(context);

    assertEquals(2, authors.size());
}

5.4. Atualizando um objeto

O processo de atualização também é fácil, mas precisamos primeiro ter o objeto desejado antes de modificar suas propriedades e aplicá-lo ao banco de dados:

@Test
public void whenUpdating_thenWeGetAnUpatedeAuthor() {
    Author author = context.newObject(Author.class);
    author.setName("Paul");
    context.commitChanges();

    Author expectedAuthor = ObjectSelect.query(Author.class)
      .where(Author.NAME.eq("Paul"))
      .selectOne(context);
    expectedAuthor.setName("Garcia");
    context.commitChanges();

    assertEquals(author.getName(), expectedAuthor.getName());
}

5.5. Anexando um objeto

Podemos atribuir um artigo a um autor:

@Test
public void whenAttachingToArticle_thenTheRelationIsMade() {
    Author author = context.newObject(Author.class);
    author.setName("Paul");

    Article article = context.newObject(Article.class);
    article.setTitle("My post title");
    article.setContent("The content");
    article.setAuthor(author);

    context.commitChanges();

    Author expectedAuthor = ObjectSelect.query(Author.class)
      .where(Author.NAME.eq("Smith"))
      .selectOne(context);

    Article expectedArticle = (expectedAuthor.getArticles()).get(0);

    assertEquals(article.getTitle(), expectedArticle.getTitle());
}

5.6. Excluindo um Objeto

A exclusão de um objeto salvo o remove completamente do banco de dados, depois disso, veremosnull como resultado da consulta:

@Test
public void whenDeleting_thenWeLostHisDetails() {
    Author author = context.newObject(Author.class);
    author.setName("Paul");
    context.commitChanges();

    Author savedAuthor = ObjectSelect.query(Author.class)
      .where(Author.NAME.eq("Paul"))
      .selectOne(context);
    if(savedAuthor != null) {
        context.deleteObjects(author);
        context.commitChanges();
    }

    Author expectedAuthor = ObjectSelect.query(Author.class)
      .where(Author.NAME.eq("Paul"))
      .selectOne(context);

    assertNull(expectedAuthor);
}

5.7. Excluir todos os registros de uma aula

Também é possível eliminar todos os registros de uma tabela usandoSQLTemplate, aqui fazemos isso após cada método de teste para sempre ter um banco de dados vazio antes de cada teste ser iniciado:

@After
public void deleteAllRecords() {
    SQLTemplate deleteArticles = new SQLTemplate(
      Article.class, "delete from article");
    SQLTemplate deleteAuthors = new SQLTemplate(
      Author.class, "delete from author");

    context.performGenericQuery(deleteArticles);
    context.performGenericQuery(deleteAuthors);
}

6. Conclusão

Neste tutorial, nos concentramos em usar o Apache Cayenne ORM para demonstrar facilmente como fazer operações CRUD com um relacionamentoone-to-many.

Como sempre, o código-fonte deste artigo pode ser encontradoover on GitHub.