Consulta avançada no Apache Cayenne

Consulta avançada no Apache Cayenne

1. Visão geral

Previously, nos concentramos em como começar a usar o Apache Cayenne.

Neste artigo, vamos cobrir como escrever consultas simples e avançadas com o ORM.

2. Configuração

A configuração é semelhante à usada no artigo anterior.

Além disso, antes de cada teste, salvamos três autores e, no final, os removemos:

  • Paul Xavier

  • Paul Smith

  • Vicky Sarra

3. ObjectSelect

Vamos começar de forma simples e ver como podemos obter todos os autores com nomes contendo “Paulo”:

@Test
public void whenContainsObjS_thenWeGetOneRecord() {
    List authors = ObjectSelect.query(Author.class)
      .where(Author.NAME.contains("Paul"))
      .select(context);

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

A seguir, vamos ver como podemos aplicar um tipo de consulta LIKE que não diferencia maiúsculas de minúsculas na coluna de nome do autor:

@Test
void whenLikeObjS_thenWeGetTwoAuthors() {
    List authors = ObjectSelect.query(Author.class)
      .where(Author.NAME.likeIgnoreCase("Paul%"))
      .select(context);

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

Em seguida, a expressãoendsWith() retornará apenas um registro, pois apenas um autor possui o nome correspondente:

@Test
void whenEndsWithObjS_thenWeGetOrderedAuthors() {
    List authors = ObjectSelect.query(Author.class)
      .where(Author.NAME.endsWith("Sarra"))
      .select(context);
    Author firstAuthor = authors.get(0);

    assertEquals(authors.size(), 1);
    assertEquals(firstAuthor.getName(), "Vicky Sarra");
}

Um mais complexo é consultar autores cujos nomes estão em uma lista:

@Test
void whenInObjS_thenWeGetAuthors() {
    List names = Arrays.asList(
      "Paul Xavier", "pAuL Smith", "Vicky Sarra");

    List authors = ObjectSelect.query(Author.class)
      .where(Author.NAME.in(names))
      .select(context);

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

Onin é o oposto, aqui apenas “Vicky” estará presente no resultado:

@Test
void whenNinObjS_thenWeGetAuthors() {
    List names = Arrays.asList(
      "Paul Xavier", "pAuL Smith");
    List authors = ObjectSelect.query(Author.class)
      .where(Author.NAME.nin(names))
      .select(context);
    Author author = authors.get(0);

    assertEquals(authors.size(), 1);
    assertEquals(author.getName(), "Vicky Sarra");
}

Observe que esses dois códigos a seguir são iguais, pois ambos criarão uma expressão do mesmo tipo com o mesmo parâmetro:

Expression qualifier = ExpressionFactory
  .containsIgnoreCaseExp(Author.NAME.getName(), "Paul");
Author.NAME.containsIgnoreCase("Paul");

Aqui está uma lista de algumas expressões disponíveis nas classesExpressioneExpressionFactory:

  • likeExp: para construir a expressão LIKE

  • likeIgnoreCaseExp: usado para construir a expressão LIKE_IGNORE_CASE

  • containsExp: uma expressão para uma consulta LIKE com o padrão correspondendo a qualquer lugar da string

  • containsIgnoreCaseExp: igual acontainsExp, mas usando uma abordagem que não diferencia maiúsculas de minúsculas

  • startsWithExp: o padrão deve corresponder ao início da string

  • startsWithIgnoreCaseExp: semelhante aostartsWithExp, mas usando a abordagem que não diferencia maiúsculas de minúsculas

  • endsWithExp: uma expressão que corresponde ao final de uma string

  • endsWithIgnoreCaseExp: uma expressão que corresponde ao final de uma string usando a abordagem que não diferencia maiúsculas de minúsculas

  • expTrue: para a expressão booleanatrue

  • expFalse: para a expressão booleanafalse

  • andExp: usado para encadear duas expressões com o operadorand

  • orExp: encadear duas expressões usando o operadoror

Mais testes escritos estão disponíveis no código-fonte do artigo, por favor verifique o repositórioGithub.

4. SelectQuery

É o tipo de consulta mais usado em aplicativos de usuário. OSelectQuery descreve uma API simples e poderosa que atua como a sintaxe SQL, mas ainda com objetos Java e métodos seguidos com padrões de construtor para construir expressões mais complexas.

Aqui, estamos falando sobre uma linguagem de expressão em que construímos consultas usando classesExpression (para construir expressões) aka qualificador eOrdering (para classificar resultados) que são convertidas em SQL nativo pelo ORM.

Para ver isso em ação, reunimos alguns testes que mostram na prática como construir algumas expressões e dados de classificação.

Vamos aplicar uma consulta LIKE para obter Autores com nomes como “Paul”:

@Test
void whenLikeSltQry_thenWeGetOneAuthor() {
    Expression qualifier
      = ExpressionFactory.likeExp(Author.NAME.getName(), "Paul%");
    SelectQuery query
      = new SelectQuery(Author.class, qualifier);

    List authorsTwo = context.performQuery(query);

    assertEquals(authorsTwo.size(), 1);
}

Isso significa que se você não fornecer nenhuma expressão para a consulta (SelectQuery), o resultado será todos os registros da tabela Autor.

Uma consulta semelhante pode ser realizada usando a expressãocontainsIgnoreCaseExp para obter todos os autores com o nome contendo Paulo, independentemente da caixa das letras:

@Test
void whenCtnsIgnorCaseSltQry_thenWeGetTwoAuthors() {
    Expression qualifier = ExpressionFactory
      .containsIgnoreCaseExp(Author.NAME.getName(), "Paul");
    SelectQuery query
      = new SelectQuery(Author.class, qualifier);

    List authors = context.performQuery(query);

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

Da mesma forma, vamos obter autores com nomes que contenham "Paul", em uma forma que não diferencia maiúsculas de minúsculas (containsIgnoreCaseExp) e com o nome que termina (endsWithExp) com a letra h:

@Test
void whenCtnsIgnorCaseEndsWSltQry_thenWeGetTwoAuthors() {
    Expression qualifier = ExpressionFactory
      .containsIgnoreCaseExp(Author.NAME.getName(), "Paul")
      .andExp(ExpressionFactory
        .endsWithExp(Author.NAME.getName(), "h"));
    SelectQuery query = new SelectQuery(
      Author.class, qualifier);
    List authors = context.performQuery(query);

    Author author = authors.get(0);

    assertEquals(authors.size(), 1);
    assertEquals(author.getName(), "pAuL Smith");
}

Uma ordem crescente pode ser executada usando a classeOrdering:

@Test
void whenAscOrdering_thenWeGetOrderedAuthors() {
    SelectQuery query = new SelectQuery(Author.class);
    query.addOrdering(Author.NAME.asc());

    List authors = query.select(context);
    Author firstAuthor = authors.get(0);

    assertEquals(authors.size(), 3);
    assertEquals(firstAuthor.getName(), "Paul Xavier");
}

Aqui, em vez de usarquery.addOrdering(Author.NAME.asc()),, também podemos usar a classeSortOrder para obter a ordem crescente:

query.addOrdering(Author.NAME.getName(), SortOrder.ASCENDING);

Relativamente, há a ordem decrescente:

@Test
void whenDescOrderingSltQry_thenWeGetOrderedAuthors() {
    SelectQuery query = new SelectQuery(Author.class);
    query.addOrdering(Author.NAME.desc());

    List authors = query.select(context);
    Author firstAuthor = authors.get(0);

    assertEquals(authors.size(), 3);
    assertEquals(firstAuthor.getName(), "pAuL Smith");
}

Como vimos no exemplo anterior - outra maneira de definir essa ordem é:

query.addOrdering(Author.NAME.getName(), SortOrder.DESCENDING);

5. SQLTemplate

SQLTemplate também é uma alternativa que podemos usar com o Cayenne para não usar consultas de estilo de objeto.

Construir consultas comSQLTemplate é diretamente relativo à gravação de instruções SQL nativas com alguns parâmetros. Vamos implementar alguns exemplos rápidos.

Aqui está como excluímos todos os autores após cada teste:

@After
void deleteAllAuthors() {
    SQLTemplate deleteAuthors = new SQLTemplate(
      Author.class, "delete from author");
    context.performGenericQuery(deleteAuthors);
}

Para encontrar todos os Autores registrados, só precisamos aplicar a consulta SQLselect * from Author e veremos diretamente que o resultado está correto, pois temos exatamente três autores salvos:

@Test
void givenAuthors_whenFindAllSQLTmplt_thenWeGetThreeAuthors() {
    SQLTemplate select = new SQLTemplate(
      Author.class, "select * from Author");
    List authors = context.performQuery(select);

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

A seguir, vamos buscar a Autora com o nome “Vicky Sarra”:

@Test
void givenAuthors_whenFindByNameSQLTmplt_thenWeGetOneAuthor() {
    SQLTemplate select = new SQLTemplate(
      Author.class, "select * from Author where name = 'Vicky Sarra'");
    List authors = context.performQuery(select);
    Author author = authors.get(0);

    assertEquals(authors.size(), 1);
    assertEquals(author.getName(), "Vicky Sarra");
}

6. EJBQLQuery

A seguir, vamos consultar os dados por meio deEJBQLQuery, que foi criado como parte de um experimento para adotar a Java Persistence API em Cayenne.

Aqui, as consultas são aplicadas com um estilo de objeto parametrizado; vamos dar uma olhada em alguns exemplos práticos.

Primeiro, a pesquisa de todos os autores salvos será assim:

@Test
void givenAuthors_whenFindAllEJBQL_thenWeGetThreeAuthors() {
    EJBQLQuery query = new EJBQLQuery("select a FROM Author a");
    List authors = context.performQuery(query);

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

Vamos pesquisar o Autor novamente com o nome “Vicky Sarra”, mas agora comEJBQLQuery:

@Test
void givenAuthors_whenFindByNameEJBQL_thenWeGetOneAuthor() {
    EJBQLQuery query = new EJBQLQuery(
      "select a FROM Author a WHERE a.name = 'Vicky Sarra'");
    List authors = context.performQuery(query);
    Author author = authors.get(0);

    assertEquals(authors.size(), 1);
    assertEquals(author.getName(), "Vicky Sarra");
}

Um exemplo ainda melhor é atualizar o autor:

@Test
void whenUpdadingByNameEJBQL_thenWeGetTheUpdatedAuthor() {
    EJBQLQuery query = new EJBQLQuery(
      "UPDATE Author AS a SET a.name "
      + "= 'Vicky Edison' WHERE a.name = 'Vicky Sarra'");
    QueryResponse queryResponse = context.performGenericQuery(query);

    EJBQLQuery queryUpdatedAuthor = new EJBQLQuery(
      "select a FROM Author a WHERE a.name = 'Vicky Edison'");
    List authors = context.performQuery(queryUpdatedAuthor);
    Author author = authors.get(0);

    assertNotNull(author);
}

Se quisermos apenas selecionar uma coluna, devemos usar esta consulta“select a.name FROM Author a”. Mais exemplos estão disponíveis no código-fonte do artigo emGithub.

7. SQLExec

SQLExec também é uma nova API de consulta fluente introduzida a partir da versão M4 do Cayenne.

Uma inserção simples é assim:

@Test
void whenInsertingSQLExec_thenWeGetNewAuthor() {
    int inserted = SQLExec
      .query("INSERT INTO Author (name) VALUES ('example')")
      .update(context);

    assertEquals(inserted, 1);
}

Em seguida, podemos atualizar um autor com base em seu nome:

@Test
void whenUpdatingSQLExec_thenItsUpdated() {
    int updated = SQLExec.query(
      "UPDATE Author SET name = 'example' "
      + "WHERE name = 'Vicky Sarra'")
      .update(context);

    assertEquals(updated, 1);
}

Podemos obter mais detalhes emdocumentation.

8. Conclusão

Neste artigo, vimos várias maneiras de escrever consultas simples e mais avançadas usando o Cayenne.

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