Wednesday, September 02, 2015

Notificação deploy no New Relic com informações do Git

O New Relic é uma ferramenta excelente para acompanhar a saúde da aplicação. Ela oferece diversas funcionalidades bem interessantes, como a notificação de deploy. Quando você avisa ao New Relic que um deploy foi realizado, ele cria uma marcação nos dashboards de acompanhamento, de forma que é possível identificar que a degradação na performance na aplicação ocorreu após o deploy da versão X.

De acordo com a documentação do New Relic é possível enviar a notificação via o agente ou via POST HTTP p/ API do New Relic (curl no Linux). Eu prefiro a segunda alternativa, além de mais simples, é mais fácil de colocar na pipeline.

Uma estratégia é criar um script que coleta informações como o comentário, o autor e a revisão do último commit no Git e envia para o New Relic. A seguir um exemplo de como isso poderia ser implementado:
#!/bin/sh

API_KEY=CHAVE_DA_SUA_APLICACAO_NO_NEW_RELIC
APP_NAME=NOME_DA_SUA_APLICACAO_NO_NEW_RELIC

DESCRIPTION=$(git log -1 --pretty=format:%s)
AUTHOR=$(git log -1 --pretty=format:%cn)
REVISION=$(git log -1 --pretty=format:%T)

echo "${APP_NAME}: notificando o NewRelic o deploy com commit [${REVISION}]"

curl -H "x-api-key:${API_KEY}" -d "deployment[app_name]=${APP_NAME}" \
  -d "deployment[description]=$DESCRIPTION" -d"deployment[revision]=$REVISION" \
  -d "deployment[user]=$AUTHOR" https://api.newrelic.com/deployments.xml

Atribua para as variáveis API_KEY e APP_NAME os valores adequados, de acordo com suas configurações. A resposta da API (POST) é um xml com dados da notificação.

www.yaw.com.br

Wednesday, August 26, 2015

Fazendo fetch via Criteria da JPA sem Metamodel Class

Outra dica relacionada a API de Criteria da JPA 2. A questão agora é: como realizar uma consulta flexível, com critérios variados, carregar os dados de uma entidade complementar (fetch) sem usar a classe gerada via Metamodel.

Como exemplo vou usar uma estrutura bem simples, duas entidades: Categoria e Mercadoria. O clássico relacionamento One-To-Many, sendo que uma Categoria possui várias Mercadoria(s). Nesse caso, a Categoria é a entidade raiz para construção da consulta.

O cenário é: gerar um relatório com as Categorias cuja as Mercadorias tenham preço entre R$ 500,00 e R$ 899,00. Além das informações das Categorias, o relatório também deve exibir as informações das Mercadorias que se enquadram nessa faixa de valores.

O pedaço de código a seguir é uma alternativa para implementar essa funcionalidade:
public final class CategoriaSpecification {
  
  public static Specification<Categoria> byPrecoComMercadoria(BigDecimal precoDe, 
      BigDecimal precoAte) {
    return new Specification<Categoria>() {
      @Override
      public Predicate toPredicate(Root<Categoria> root,
          CriteriaQuery<?> query, CriteriaBuilder builder) {
        FetchParent<Categoria, Mercadoria> fetch = root.fetch("mercadorias");
        Join<Categoria, Mercadoria> join = 
          (Join<Categoria, Mercadoria>) fetch; //truque

        return builder.between(join.get("preco"),
          precoDe, precoAte);
      }
    };
  }
  ...
}

O "truque" é usar o método fetch em root, mas converter a visão do objeto para Join. Os tipos Join e Fetch tem em comum a interface FetchParent. Com Join é possível definir predicados na construção da query. Note que nesse exemplo, eu uso o componente de Specification do Spring Data JPA.

www.yaw.com.br

Thursday, August 20, 2015

Usando Criteria da JPA 2 com Spring Data JPA

A flexibilidade do Spring Data JPA para desenvolver componentes de persistência e acesso a dados não é novidade. Nesse post vou descrever como utilizar o Criteria da JPA 2.0 em conjunto com o Spring Data JPA para gerar consultas avançadas. Essa abordagem é uma outra alternativa comparada ao QueryDSL.

O contrato Specification determina que um predicado (condição SQL) é criado a partir dos elementos Root, CriteriaQuery e CriteriaBuilder. Esses três elementos fazem parte da API de Criteria do JPA 2, mas são controlados e injetados pelo Spring.

O trecho a seguir, demonstra uma Specification para aplicar o filtro da categoria na consulta da entidade Mercadoria:

public final class MercadoriaSpecification {

  public static Specification<Mercadoria> byNome(String name) {
    return new Specification<Mercadoria>() {
      @Override
      public Predicate toPredicate(Root<Mercadoria> root,
          CriteriaQuery<?> query, CriteriaBuilder builder) {
        return builder.like(root.<String>get("nome"), 
            String.format("%s%", name.trim()));
      }
    };
  }
  ...
}

Essa Specification será encaminhada para o repositório, no momento da consulta. Veremos isso depois de analisar o repositório.

A interface Repository, sofre uma pequena modificação. Ela deve estender a interface JpaSpecificationExecutor, veja:

@Repository
public interface MercadoriaRepository
    extends JpaRepository<Mercadoria, Long>, JpaSpecificationExecutor<Mercadoria> {
}

Através dessa extensão é possível utilizar Specification nas consultas da entidade. O próximo trecho é usar o repositório p/ fazer a consulta de Mercadoria(s) de acordo com o predicado definido na Specification:

@RestController
@RequestMapping(value="/")
public class MercadoriaController {

  @Autowired
  private MercadoriaRepository repository;

  @RequestMapping(method = RequestMethod.GET, value="/{nome}")
  public List<Mercadoria> listByNome(@PathVariable String nome) {
    Specification<Mercadoria> specification = MercadoriaSpecification.byNome(nome);
    return Lists.newArrayList(repository.findAll(specification));
  }
  ...
}

Além da interface Specification, o Spring Data JPA também oferece a classe Specifications para facilitar a composição de múltiplos predicados. Subi um gist no meu GitHub com os trechos de código desse post e um outro exemplo de consulta com vários predicados simulando um formulário de filtros.

A referência desse post vem do blog do Spring.io.

www.yaw.com.br

Tuesday, June 30, 2015

Integrando o Docker com Maven e Gradle

Para otimizar o processo de build em aplicações Java é possível incluir no ciclo de construção uma etapa para construir imagens do Docker. Isso pode ser feito no Maven ou Gradle através de plugins.

Docker no build do Maven

Trecho do pom.xml com o plugin do Docker:
...

<properties>
  <docker.image.prefix>yaw</docker.image.prefix>
</properties>

...

<build>
  <plugins>
    <plugin>
      <groupId>com.spotify</groupId>
      <artifactId>docker-maven-plugin</artifactId>
      <version>0.2.3</version>
      <configuration>
        <imageName>${docker.image.prefix}/${project.artifactId}</imageName>
        <dockerDirectory>src/main/docker</dockerDirectory>
        <resources>
          <resource>
            <targetPath>/</targetPath>
            <directory>${project.build.directory}</directory>
            <include>${project.build.finalName}.jar</include>
          </resource>
        </resources>
      </configuration>
    </plugin>
  </plugins>
</build>

O pessoal do Spotify desenvolveu o plugin docker-maven-plugin. A propriedade docker.image.prefix é usada como prefixo para o nome da imagem, nesse caso yaw. O restante é o próprio nome do artefato. Na tag dockerDirectory indico o diretório aonde fica contido o arquivo Dockerfile, com as diretrizes para geração da imagem. Na tag resources indicamos os recursos que são compartilhados na execução do Docker.

Para criar a imagem Docker, a partir do ciclo de build do Maven, executamos o seguinte comando:
mvn clean package docker:build

Nesse caso a imagem é gerada após a construção do artefato (package). Outra abordagem seria configurar o Maven para construir a imagem implicitamente a partir do mvn package. Para isso é necessário customizar a tag execution com phase e goals.

A pŕoxima etapa seria criar o Container Docker e executar a aplicação. Vou demonstrar isso depois de apresentar o plugin do Gradle.

Docker no build do Gradle

Trecho do build.grade com plugin do Docker:
buildscript {
  ...
  dependencies {
    ...
    classpath('se.transmode.gradle:gradle-docker:1.2')
  }
}

...

group = 'yaw'
apply plugin: 'docker'

task buildDocker(type: Docker, dependsOn: build) {
  push = false
  applicationName = jar.baseName
  dockerfile = file('src/main/docker/Dockerfile')
  doFirst {
    copy {
      from jar
      into stageDir
    }
  }
}

No Gradle usei o plugin gradle-docker. Nele indico o caminho do Dockerfile, o prefixo e o nome da imagem. A próxima etapa é gerar a imagem em conjunto com o build Gradle:
gradle build buildDocker

Como referência é possível visualizar exemplos desses dois builds no projeto querydsl-spring-data, no meu Github. Esse projeto utiliza o Spring Boot, com Tomcat embutido. A seguir eu coloco o conteúdo do Dockfile utilizado para construir a imagem para executar esse projeto:
FROM edermag/ubuntu-java-8-dev

VOLUME /tmp
ADD querydsl-spring-data.jar app.jar
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

A imagem herda a estrutura com o JDK 8 da Oracle no Ubuntu 14.04, e faz o startup do projeto com Spring Boot. A última etapa é criar o Container a partir da imagem construida via Maven ou Gradle. Execute a instrução:
docker run -p 8080:8080 -t yaw/querydsl-spring-data

A aplicação será inicializada (Spring Boot) dentro do Container Docker, usando a porta 8080. Para encerrar o Container será necessário executar o comando docker stop indicando o apelido do Container.

Como referência indico o guia do Spring IO, descrevendo como usar o Docker com Spring Boot.

www.yaw.com.br

Monday, June 22, 2015

Explorando o Docker para construir ambientes Java

Containers

Agilidade, produtividade e qualidade são termos constantes no contexto de desenvolvimento de sistemas. O que ao primeiro momento, de forma enganosa, nos remete apenas a escrita de código em si. Na verdade existem diversos outros fatores que impactam diretamente na agilidade de um time de desenvolvimento. Fatores que ultrapassam as fronteiras da escrita de código, como por exemplo o provisionamento da infra-estrutura.

Analisando a questão do provisionamento, a virtualização foi uma das alternativas para reduzir os custos e otimizar a infra-estrutura, principalmente com o crescimento e adoção dos serviços em Cloud Computing (PaaS). Mas existem outras alternativas nesse mesmo campo. O Docker, por exemplo, é uma plataforma para construir e manter ambientes para a excecução de sistemas distribuídos.

A plataforma é formada por diversor módulos, como o Docker Engine e o Docker Hub. Ela opera sob o conceito de containers do LXC sigla de Linux Containers, que atua de forma diferente das máquinas virtualizadas. Ao invés de isolar um o SO inteiro o Linux host compartilha seu kernel para outros processos isolados rotulados como Container. O Docker Engine é uma camada sobre o LXC, e permite que usuários criem e executem um ou vários Containers, simulando um SO 'puro', abaixo de um mesmo kernel compartilhado pelo Linux host. Um processo mais leve do que a virtualização de máquinas.

Dessa forma eu poderia montar dois ambientes Java distintos, na mesma máquina, em poucos passos. Por exemplo: o primeiro Container configurado para executar uma aplicação usando o Java 7, Tomcat e Gradle no CentOS; o segundo Container configurado para executar uma aplicação usando o Java 8, Jetty e Maven no Ubuntu. Esses dois Containers poderiam ser mantidos um Mac OS X Yosemite como host (no Mac OS ou Windows será necessário instalar um boot2docker).

No Docker o Container é tratado como o artefato, ou seja, o ambiente pode ser versionado e distribuído da mesma forma como fazemos com o código fonte da aplicação. Antes de explorar o Docker, é importante compreender como funcionam três tipos de componentes:
  1. Image: uma imagem é um template que define a estrutura para Containers.
  2. Container: o Container simula o ambiente de execução como um todo, é uma 'instância' da imagem.
  3. Repository / registry: registro aonde as imagens são mantidas (versionamento). O Docker Hub é uma central aonde imagens podem ser persistidas e compartilhadas.

Primeiro Container Java

A partir desse ponto irei demonstrar como trabalhar com Containers do Docker, construindo ambientes para execução de aplicações Java. Para simular os passos seguintes é necessário que você instale o Docker e faça o HelloWorld para testar a ferramenta.

No início vou trabalhar com uma imagem do Java 8, disponibilizada (public) no Docker Hub. Essa imagem trata-se de um Ubuntu 14.04 com o JDK versão 8, disponibilizado pelo OpenJDK. Para baixar a imagem do Docker Hub use a instrução:
$ sudo docker pull java:8

No comando docker pull informei o conteúdo java:8, aonde java representa o repositório, : o separador, e 8 a versão. Após concluir o download, é possível verificar a imagem através do comando:
$ sudo docker images

A lista apresenta o repositório, a versão e tamanho da imagem. O id é uma informação que pode ser usada para remover a imagem do host. A próxima etapa é criar um Container a partir dessa imagem. e usar de alguma forma o JDK: 
$ sudo docker run java:8 java -version

O comando docker run cria a instância da imagem, o Container. O conteúdo java:8 indica qual imagem deve ser usada, e por fim a instrução que deverá ser processada pelo container. Utilizei o comando java -version como exemplo. Assim que a versão do Java é impressa na console, o processo Docker é encerrado.

Para listar todos os Containers, ativos ou que foram concluídos, use a instrução:
$ sudo docker ps -a

Note que a primeira linha exibe o Container criado para a imagem java:8, veja o valor na coluna NAMES. Trata-se de um apelido gerado pelo Docker para identificarmos o Container. Caso você execute novamente o comando docker run, um novo container será criado. Para evitar isso basta usar a opção --rm, que descartará o Container no fim da execução.

A próxima instrução criará um Container para executar uma classe Java. Como exemplo usei o clássico HelloWorld, que exibe uma mensagem na console. Importante notar que a classe já foi compilada, e o arquivo class está no filesystem do host, fora do Container:
$ sudo docker run --rm -v "$PWD":/home/user/test -w /home/user/test java:8 
  java OlaDocker

A opção -v indica o volume que é carregado para o Container, e -w o diretório de trabalho. No exemplo as duas opções apontam para o mesmo diretório (/home/user/test), que contém o arquivo OlaDocker.class. Ajuste a instrução indicando o path e o nome adequado da sua classe Java.

Docker para dev Java Web

Próximo passo é executar uma aplicação Java Web com Docker. Para isso vou usar uma imagem que criei com as seguintes característica: Tomcat 8, JDK 8 (Oracle), Maven 3.3 e Ubuntu 14.04. O conteúdo a seguir é um exemplo do Dockerfile, usando a imagem edermag/tomcat-8-dev, disponível no Docker Hub
FROM edermag/tomcat-8-dev

WORKDIR /source

ENV app appJavaWeb
ADD pom.xml /source/pom.xml
ADD src /source/src

RUN mvn clean package && \
    mv /source/target/$app-0.0.1-SNAPSHOT.war $CATALINA_HOME/webapps/          

CMD ["catalina.sh", "run"]

Importante: o Tomcat utiliza a porta 8080 configurada na imagem edermag/tomcat-8-dev.

Esse exemplo assume que o projeto segue o layout do Maven. Sendo assim, o arquivo Dockerfile deve ficar no mesmo diretório do arquivo pom.xml. Nesse arquivo colocamos as instruções para que o Docker construa uma imagem, local, usando como base a imagem edermag/tomcat-8-dev. As instruções são:
  • Criar o diretório /source, e usá-lo como diretório de trabalho (manipulação) do Container;
  • Setar uma variável de ambiente com o nome do artefato gerado pelo Maven;
  • Copiar o arquivo pom.xml do host para a imagem. Também copiar todo o código fonte (diretório src) do host para a imagem;
  • Executar o Maven para construir o projeto e fazer o deploy do WAR no Tomcat;
  • Por fim inicializar o Tomcat;
Para criar a imagem no Docker a partir desse arquivo, execute a seguinte instrução no diretório raiz da aplicação:
$ sudo docker build -t javaweb .

De acordo com a instrução acima, o nome da imagem é javaweb. Durante a geração dessa imagem, o build e o deploy serão realizados. Caso o código seja modificado a imagem deverá ser gerada novamente (nova versão). Após executar o comando acima, verifique novamente as imagens instaladas no host através do comando: docker images. O próximo passo é instanciar o Container da imagem javaweb:
$ sudo docker run -d -p 8081:8080 javaweb

Com essa instrução o Docker cria o Container em modo Daemon, via opção -d, e aloca a porta 8081 para redirecionar a porta 8080 da imagem, via opção -p. Verifique o NAME (apelido) do Container através do comando: docker ps.

Como o build e deploy são realizados durante a geração da imagem, a execução do Container irá inicializar o Tomcat. Para visualizar os logs do Tomcat execute a instrução (use o NAME do Container):
$ sudo docker logs {coloqueOConteudoDoNAME}

Para encerrar a execução do Container execute a instrução:
$ sudo docker logs {coloqueOConteudoDoNAME}

Com pequenos ajustes seria possível criar uma imagem (e Container) para executar a aplicação Java Web no Jetty.

www.yaw.com.br

Tuesday, May 26, 2015

Java e NoSQL: usando o MongoDB com Spring Data e QueryDSL

Interessante como algumas tecnologias conseguem ser úteis e flexíveis. Esse é um dos pontos que eu mais admiro no Spring Data. É possível usar seus recursos, conceito e o modelo de componentes em diferentes mecanismos de persistência. Nesse post eu volto a escrever sobre o Spring Data e o QueryDSL, mas em outro contexto, minha idéia é demonstrar como usar essas tecnologias para persistir dados no MongoDB.

Como referência vou utilizar um projeto que compartilhei no Github. Trata-se de uma aplicacação simples com uma página de listagem e pesquisa com filtros variados. A simulação de um cadastro de Mercadoria, um pojo marcado com @Entity. Anotação do Morphia, tecnologia para mapeamento objeto em documento, fazendo o meio campo entre objetos Java e coleções do MongoDB. Na verdade o QueryDSL utiliza o Morphia para gerar a estrutura de consultas para o MongoDB. O plugin do QueryDSL para gerar a estrutura de consultas, por entidade, utiliza a API do Morphia. Veja as configurações do plugin APT do QueryDSL no pom.xml ou build.gradle.

O papel do QueryDSL nesse projeto é pontual, criar o Predicate com as condições variadas para a consulta das coleções no MongoDB. A consulta varia de acordo com os filtros preenchidos pelo formulário de pesquisa (lista de mercadorias). O código a seguir, da classe MercadoriaQuery, demonstra como construir o Predicate:
  ...
  public static Predicate whereByCriterio(FiltrosPesquisaMercadoria filtros) {
    QMercadoria mercadoria = QMercadoria.mercadoria;
    BooleanBuilder builder = new BooleanBuilder();
    if (!Strings.isNullOrEmpty(filtros.getDescricaoMercadoria())) {
      builder.and(mercadoria.descricao.startsWithIgnoreCase(
        filtros.getDescricaoMercadoria()));
    }
    if (!Strings.isNullOrEmpty(filtros.getNomeMercadoria())) {
      builder.and(mercadoria.nome.startsWithIgnoreCase(
        filtros.getNomeMercadoria()));
    }
    if (filtros.getPrecoDe() != null && filtros.getPrecoDe() > 0) {
      builder.and(mercadoria.preco.goe(filtros.getPrecoDe()));
    }
    if (filtros.getPrecoAte() != null && filtros.getPrecoAte() > 0) {
      builder.and(mercadoria.preco.loe(filtros.getPrecoAte()));
    }
    if (!Strings.isNullOrEmpty(filtros.getCategoria())) {
      builder.and(mercadoria.categoria.descricao.startsWithIgnoreCase(
        filtros.getCategoria()));
    }
    return builder;
  } 
  ...

Além das condições para construir a consulta, é importante notar outra característica sobre front-end: a lista de mercadorias apresenta os registros organizados por página. O Spring Data é o responsável por resolver a paginação da consulta com MongoDB. Na classe CriteriaUtil, definos o método para criar o Pageable, com informações da página, quantidade de registros e campo de ordenação. Além disso, essa classe também define um método para montar uma expressão like p/ o MongoDB via QueryDSL.
public final class CriteriaUtil {

  public static Pageable buildPageRequest(int page, int rows, 
      String sortBy) {
    Sort sort = new Sort(Sort.Direction.ASC, sortBy);
    return new PageRequest(page, rows, sort);
  }

  public static BooleanExpression likeWithLowerCase(
      StringExpression field, String expression) {
    String filterValue = String.format("%s%s",expression.toLowerCase(), "%");
    return field.toLowerCase().like(filterValue);
  }
}

Olhando para o principal componente do Spring Data, temos o repositório MercadoriaRepository. Um contrato que define as operações de persistência sobre a coleção Mercadoria. Parecido com o que acontece com o JPA o repositório extende MongoRepository, interface com as operações de persistência (CRUD) de uma determinada coleção. Já outra extensão, de QueryDSLPredicateExecutor, permite que repositório tenha a capacidade de realizar consultas aplicando Predicates do QueryDSL. Veja o código do repositório:
@Repository
public interface MercadoriaRepository
  extends MongoRepository<Mercadoria, Long>, 
    QueryDslPredicateExecutor<Mercadoria> {

  List<Mercadoria> findByDescricaoLike(String descricao);

  @Query(value="{ 'quantidade': { $gte: ?0 } }")
  List<Mercadoria> findByQuantidadeEqualOrGreather(
    Integer quantidade);

  default List<CategoriaGroup> groupByCategorias(
      MongoTemplate template) {
    Aggregation agg = newAggregation(
      group("categoria.descricao").count().as("qtdMercadorias"),
      project("qtdMercadorias").and("categoria.descricao").previousOperation(),
      sort(Sort.Direction.DESC, "qtdMercadorias"));
    AggregationResults<CategoriaGroup> groupResults = 
      template.aggregate(agg, Mercadoria.class, CategoriaGroup.class);
    return groupResults.getMappedResults();
  }
}

O Spring Data constrói o proxy que implementa o contrato repostiório, de forma transparente para o restante da aplicação.

Além dos métodos padrões, das duas interfaces citadas, defino três consultas customizadas:

  • findByDescricaoLike(String): o Spring Data gera o código da consulta, respeitando o nome do método. Nesse caso usando como filtro o campo descricao e operador de consulta like, aplicando o argumento informado. Esse um recurso do Spring Data, independente do MongoDB, funciona tanto em banco de dados NoSQL quanto em relacionais.
  • findByQuantidadeEqualOrGreather(Integer): nesse caso o Spring Data implementa o método usando a consulta definida via anotação @Query.
  • groupByCategorias: método utiliza API do Spring Data para gerar consultas com funções agregadoras do MongoDB (link). Um contador simples de Mercadorias, agrupados por Categoria, um campo (embedded) do documento.


Na controller MercadoriaController, defino diversos métodos que operam sobre o repositório. O destaque fica para o método list, que é acionado para consultar as Mercadorias de acordo com os filtros do formulário de pesquisa.
  ...
  @RequestMapping(method = RequestMethod.GET)
  public PesquisaMercadorias list(FiltrosPesquisaMercadoria filtros) {
    Pageable page = buildPageRequest(filtros.getPagina(), 
      filtros.getLinhas(), filtros.getOrdem());

    Predicate predicate = whereByCriterio(filtros);
    long total = repository.count(predicate);
    List<Mercadoria> mercadorias = 
      Lists.newArrayList(repository.findAll(predicate, page));
    return new PesquisaMercadorias(total, mercadorias);
  }
  ...

No startup da aplicação realizo o setup dos dados, acesse e analise o código da classe Application.

Outros detalhes sobre o projeto:

  • O build e as dependências do projeto são controladas pelo Maven e/ou Gradle;
  • Spring MVC atua como framework web;
  • JQuery e Foundation no front-end da aplicação;


www.yaw.com.br

Tuesday, May 12, 2015

Combinando Spring Data, QueryDSL e JPA / Hibernate para persistência em Java

Mais um post falando sobre persistência em Java com QueryDSL, mas agora explorando também o Spring Data. O Spring Data é uma tecnologia criada para facilitar a criação de componentes de acesso a dados via: bancos relacionais, NoSQL,  
map-reduce e serviços baseados em cloud.

Olhando para base de dados relacionais, o Spring Data oferece componentes para JPA e JDBC. Em outro post escrevi sobre o QueryDSL, uma ferramenta para produzir consultas flexíveis encima da JPA (e outros mecanismos de persistência) com uma DSL bem interessante. Evoluindo aquela abordagem, meu objetivo é apresentar outra estratégia para persistência unindo o QueryDSL e Spring Data para JPA / Hibernate.

Compartilhei no Github um projeto que pode ser referência para esse post. Outro detalhe importante é que o projeto foi construído com o Spring Boot, que disponibiliza um Tomcat embutido. A classe Application é o ponto de entrada da aplicação, aonde ficam contidas as configurações do projeto. É o caso da anotação EnableJpaRepositories, que indica o pacote base dos componentes Repositories. O código a seguir demonstra o componente MercadoriaRepository, uma interface anotada como Repository responsável pela operações de persistência da entidade Mercadoria.
@Repository
public interface MercadoriaRepository
  extends JpaRepository<Mercadoria, Long>,
    QueryDslPredicateExecutor<Mercadoria> {
  …
}

JpaRepository é uma interface do Spring que define operações básicas de persistência sobre uma determinada entidade. Operações para CRUD, paginação e ordenação. Já a interface QueryDslPredicateExecutor atua como uma bridge entre o Spring Data e QueryDSL. Através dela é possível usar Predicates construídos via QueryDSL em consultas via Spring Data.

É possível e válido definir uma classe concreta como Repository. Mas prefiro definir esses componentes como contratos, sendo o Spring o responsável por escrever código que implementa (proxy) as interfaces. Mesmo em interface, o Spring Data permite a definição de métodos para consultas customizados.

No controller, o proxy de MercadoriaRepository é injetado pelo Spring, via anotação Autowired. A seguir trecho de código do componente MercadoriaController, com destaque para a definição do Repository e o método list, responsável por realizar a consulta de Mercadorias de acordo com os filtros informados:
@RestController
@RequestMapping(value="/")
public class MercadoriaController {
  
  @Autowired
  private MercadoriaRepository repository;
   
  @RequestMapping(method = RequestMethod.GET)
  public PesquisaMercadorias list(FiltrosPesquisaMercadoria filtros) {
    Pageable page = buildPageRequest(filtros.getPagina(), 
      filtros.getLinhas(), filtros.getOrdem());
    
    Predicate predicate = whereByCriterio(filtros);
    long total = repository.count(predicate);
    List<Mercadoria> mercadorias =
      Lists.newArrayList(repository.findAll(predicate, page));
    return new PesquisaMercadorias(total, mercadorias);
  }
  ...
}

O primeiro passo é construir Pageable com as configurações para paginação e ordenação via buildPageRequest. Depois construir o Predicate com as condições da consulta, via whereByCriterio, que será utilizado no contador de registros (informação para paginação) e para processar a consulta das mercadorias. Ambos os métodos count(Predicate) e findAll(Predicate) são definidos em QueryDSLPredicatorExecutor.

Os métodos save e delete, definidos em JpaRepository (CrudRepository), também são acionados pelo controller:
  @RequestMapping(method = RequestMethod.POST)
  public void save(Mercadoria m) {
    repository.save(m);
  }
 
  @RequestMapping(method = RequestMethod.DELETE)
  public void delete(Long mercadoriaId) {
    repository.delete(mercadoriaId);
  }

Todas as operações de persistência foram centralizadas na interface MercadoriaRepository, mas ela delega o update em batch e consulta de tuplas para MercadoriaQuery, via métodos default do Java 8.

Outros detalhes sobre o projeto:
  • O build e as dependências do projeto são controladas pelo Maven e/ou Gradle;
  • Spring MVC atua como framework web;
  • JQuery e Foundation no front-end da aplicação