Tipos de consultas JPA

Tipos de consultas JPA

1. Visão geral

Neste tutorial, discutiremos os diferentes tipos de consultasJPA. Além disso, vamos nos concentrar em comparar as diferenças entre eles e expandir os prós e contras de cada um.

2. Configuração

Em primeiro lugar, vamos definir a classeUserEntity que usaremos para todos os exemplos neste artigo:

@Table(name = "users")
@Entity
public class UserEntity {

    @Id
    private Long id;
    private String name;
    //Standard constructor, getters and setters.

}

Existem três tipos básicos de consultas JPA:

  • Query, escrito na sintaxe Java Persistence Query Language (JPQL)

  • NativeQuery, escrito em sintaxe SQL simples

  • Criteria API Query, construído de forma programática por meio de métodos diferentes

Vamos explorá-los.

3. Query

AQuery é semelhante em sintaxe ao SQL e geralmente é usado para realizar operações CRUD:

public UserEntity getUserByIdWithPlainQuery(Long id) {
    Query jpqlQuery = getEntityManager().createQuery("SELECT u FROM UserEntity u WHERE u.id=:id");
    jpqlQuery.setParameter("id", id);
    return (UserEntity) jpqlQuery.getSingleResult();
}

EsteQuery recupera o registro correspondente da tabelausers e também o mapeia para o objetoUserEntity.

Existem dois subtipos adicionais deQuery:

  • TypedQuery

  • NamedQuery

Vamos vê-los em ação.

3.1. TypedQuery

Precisamos prestar atenção à instruçãoreturn em nosso exemplo anterior. JPA não pode deduzir qual será o tipo de resultadoQuery e, como resultado, temos que lançar.

Mas,JPA provides a special Query sub-type known as a TypedQuery. Isso é sempre preferível se sabemos nosso tipo de resultadoQuery de antemão. Além disso, torna nosso código muito mais confiável e fácil de testar.

Vejamos uma alternativaTypedQuery, em comparação com nosso primeiro exemplo:

public UserEntity getUserByIdWithTypedQuery(Long id) {
    TypedQuery typedQuery
      = getEntityManager().createQuery("SELECT u FROM UserEntity u WHERE u.id=:id", UserEntity.class);
    typedQuery.setParameter("id", id);
    return typedQuery.getSingleResult();
}

Dessa forma,we get stronger typing for free, evitando possíveis exceções de casting no futuro.

3.2. NamedQuery

Embora possamos definir dinamicamente aQuery em métodos específicos, eles podem eventualmente crescer e se tornar uma base de código difícil de manter. E se pudéssemos manter as consultas de uso geral em um local centralizado e de fácil leitura?

JPA também nos cobriu isso com outro subtipoQuery conhecido comoNamedQuery.

DefinimosNamedQuery na própria classeEntity, fornecendo uma maneira centralizada, rápida e fácil de ler e encontrar as consultas relacionadas deEntity.

Todos osNamedQueries devem ter um nome exclusivo.

Vamos ver como podemos adicionar umNamedQuery à nossa classeUserEntity:

@Table(name = "users")
@Entity
@NamedQuery(name = "UserEntity.findByUserId", query = "SELECT u FROM UserEntity u WHERE u.id=:userId")
public class UserEntity {

    @Id
    private Long id;
    private String name;
    //Standard constructor, getters and setters.

}

A anotação@NamedQuery deve ser agrupada dentro de uma anotação@NamedQueries se estivermos usando Java antes da versão 8. Do Java 8 em diante, podemos simplesmente repetir a anotação@NamedQuery em nossa classeEntity.

Usar umNamedQuery é muito simples:

public UserEntity getUserByIdWithNamedQuery(Long id) {
    Query namedQuery = getEntityManager().createNamedQuery("UserEntity.findByUserId");
    namedQuery.setParameter("userId", id);
    return (UserEntity) namedQuery.getSingleResult();
}

4. NativeQuery

ANativeQuery é simplesmente uma consulta SQL. Isso nos permite liberar todo o poder de nosso banco de dados, pois podemos usar recursos proprietários não disponíveis na sintaxe restrita a JPQL.

Isso tem um custo. Perdemos a portabilidade do banco de dados de nosso aplicativo comNativeQuery porque nosso provedor JPA não consegue mais abstrair detalhes específicos da implementação ou fornecedor do banco de dados.

Vamos ver como usar umNativeQuery que produz os mesmos resultados de nossos exemplos anteriores:

public UserEntity getUserByIdWithNativeQuery(Long id) {
    Query nativeQuery
      = getEntityManager().createNativeQuery("SELECT * FROM users WHERE id=:userId", UserEntity.class);
    nativeQuery.setParameter("userId", id);
    return (UserEntity) nativeQuery.getSingleResult();
}

Devemos sempre considerar se aNativeQuery é a única opção. Most of the time, a good JPQL Query can fulfill our needs and most importantly, maintain a level of abstraction from the actual database implementation.

UsarNativeQuery não significa necessariamente nos restringir a um fornecedor de banco de dados específico. Afinal, se nossas consultas não usam comandos SQL proprietários e estão usando apenas uma sintaxe SQL padrão, a troca de provedores não deve ser um problema.

5. Criteria API Query

Criteria API queries são consultas seguras de tipo construídas de maneira programática - um pouco semelhantes às consultas JPQL na sintaxe:

public UserEntity getUserByIdWithCriteriaQuery(Long id) {
    CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
    CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(UserEntity.class);
    Root userRoot = criteriaQuery.from(UserEntity.class);
    UserEntity queryResult = getEntityManager().createQuery(criteriaQuery.select(userRoot)
      .where(criteriaBuilder.equal(userRoot.get("id"), id)))
      .getSingleResult();
    return queryResult;
}

Pode ser assustador usarCriteria consultas de API em primeira mão, mas podem ser uma ótima escolha quando precisamos adicionar elementos de consulta dinâmicos ou quando combinados comJPA Metamodel.

6. Conclusão

Neste artigo rápido, aprendemos o que são as consultas JPA, além de seu uso.

As consultas JPA são uma ótima maneira de abstrair nossa lógica de negócios de nossa camada de acesso a dados, pois podemos contar com a sintaxe JPQL e permitir que nosso provedor JPA de escolha manipule a traduçãoQuery.

Todo o código apresentado neste artigo está disponívelover on GitHub.