Search buenodc

Nesta página
  1. Importante
  2. Contextualização
  3. O que já temos
  4. Como funciona
  5. Cronograma
  6. Missão 1: Injeção de dependência
  7. Missão 2: A primeira consulta
  8. Missão 3: Consultas com queryForList e Map
  9. Missão 4: Consulta parametrizada + desafio
  10. Entrega individual

Atividade Consulta de Dados (EstanteWeb)

Disciplina: Programação para Web 3
Assunto: SELECT, ResultSet, JdbcTemplate, injeção de dependência e exibição em páginas
Professor: Prof. Me. Danilo Bueno
Grupos: duplas
Duração: 4 aulas de 50 minutos (em sala)


Importante

Antes de começar, leia o material de apoio sobre consulta de dados. É imprescindível ler antes de codar: a atividade aplica exatamente os conceitos do material. Se tiver dúvidas, releia, pergunte ao professor ou aos colegas. Você também pode consultar os slides da aula.


Contextualização

O banco de dados da EstanteWeb existe desde a semana passada, com a tabela livro populada, mas nenhuma linha de código Java o consulta ainda: o site continua exibindo os mesmos seis livros escritos direto no LivroRepository. Nesta etapa, a dupla vai fazer o acervo passar a vir de verdade do banco, o que também exige, pela primeira vez neste projeto, entregar ao Spring a responsabilidade de criar os objetos da aplicação, em vez de usar new à mão.


O que já temos

O checkpoint de partida (semana-07-inicio) traz a aplicação Spring Boot das semanas anteriores, já rodando em http://localhost:8080, com:

  • Banco MySQL com as tabelas livro (populada com 6 livros) e sugestao (vazia), criadas pelas migrações da Semana 6.
  • LivroRepository, ainda em memória: listarTodos(), contarLivros(), listarAtePreco(double), todos lendo de uma List<Livro> fixa dentro da classe.
  • LivroController, instanciando o repositório com new LivroRepository(), como em todas as semanas anteriores.
  • SugestaoController/SugestaoRepository, inalterados, ainda em memória (fora do escopo desta semana).

Como funciona

  • A atividade é feita em dupla, mas as entregas no Moodle são individuais.
  • As missões são incrementais: cada uma constrói sobre a anterior, siga na ordem.
  • Ao final de cada missão, teste no navegador antes de seguir.
  • Responda, no caminho, às perguntas em destaque (marcadas com >). Elas voltam no questionário do Moodle.

Cronograma

AulaFocoMissões
1Injeção de dependênciaMissão 1
2A primeira consulta (queryForObject)Missão 2
3Consultas com queryForList e MapMissão 3
4Consulta parametrizada + desafioMissão 4

Missão 1: Injeção de dependência

Objetivo: preparar LivroRepository e LivroController para receber o que precisam por @Autowired, em vez de criar tudo com new.

1.1. Em LivroRepository, adicione a anotação @Repository à classe. Substitua a criação manual de dados por um campo private JdbcTemplate jdbcTemplate;, anotado com @Autowired. Não remova ainda a lista em memória nem os métodos existentes: por enquanto, o campo jdbcTemplate fica sem uso.

1.2. Em LivroController, troque a criação manual (private LivroRepository repositorio = new LivroRepository();) por um campo private LivroRepository repositorio;, também anotado com @Autowired.

1.3. Rode a aplicação e acesse /livros. O comportamento deve continuar idêntico ao de antes: os dados ainda vêm da lista em memória, só a forma como os objetos são criados mudou.

Depois desta missão, quem cria o LivroRepository e o LivroController: o próprio código da aplicação, como antes, ou outra coisa? Como você percebe essa mudança rodando a aplicação?


Missão 2: A primeira consulta

Objetivo: trocar o primeiro método por uma consulta real ao banco.

2.1. Reescreva contarLivros() para executar SELECT COUNT(*) FROM livro usando jdbcTemplate.queryForObject(sql, Integer.class), no lugar de livros.size().

2.2. Teste /livros/total: o número deve continuar sendo 6 (a mesma contagem de antes, agora vinda do banco).

2.3. Pelo terminal do MySQL (docker exec, como na Semana 6), insira manualmente um sétimo livro com um INSERT INTO livro (...) VALUES (...). Recarregue /livros/total sem reiniciar a aplicação.

O número mudou para 7 sem reiniciar a aplicação nem alterar uma linha de código Java. O que isso mostra sobre de onde o dado realmente vem agora?


Missão 3: Consultas com queryForList e Map

Objetivo: trazer o acervo inteiro do banco, convertendo cada linha num objeto Livro.

3.1. Transforme a classe Livro numa classe com construtor sem argumentos e setters: adicione um campo id (tipo long), um construtor vazio (public Livro() { }) e um set para cada campo (setId, setTitulo, setAutor, setAno, setPreco). Remova o construtor antigo, que recebia todos os campos de uma vez: ele deixará de ser usado.

3.2. Ainda em Livro, crie o método estático deMap(Map<String, Object> linha), que cria um Livro vazio, preenche cada campo a partir do Map recebido (lembrando de converter valores numéricos primeiro para Number, depois para o tipo específico: ((Number) linha.get("id")).longValue(), e o mesmo para ano e preco) e o devolve.

3.3. Reescreva listarTodos() em LivroRepository para executar SELECT * FROM livro com jdbcTemplate.queryForList(sql), percorrer a lista de Map resultante com um laço, e usar Livro.deMap(linha) para montar cada objeto.

3.4. Remova, agora sim, o campo da lista em memória (List<Livro> livros = List.of(...)): nada mais no LivroRepository deve depender dele.

3.5. Teste /livros e /livros/html: os mesmos livros da semana passada devem aparecer, incluindo o do título com a tag <b>, ainda escapado no template. Repita o experimento da Missão 2.3, mas agora atualizando o título de um livro direto no banco (UPDATE livro SET titulo = ... WHERE id = ...), e confirme a mudança na página.

Nenhuma linha de LivroController ou de livros.ftlh mudou nesta missão, mas o comportamento das páginas mudou completamente. Qual princípio, estudado desde a Semana 2, explica por que isso foi possível?


Missão 4: Consulta parametrizada + desafio

Objetivo: reescrever a busca por preço com um parâmetro seguro, e agregar um novo dado com queryForObject.

4.1. Reescreva listarAtePreco(double precoMaximo) para executar SELECT * FROM livro WHERE preco <= ?, passando precoMaximo como parâmetro posicional do queryForList(...), no lugar do filtro feito em Java. Monte a lista de retorno da mesma forma que em listarTodos(): percorrendo o resultado e usando Livro.deMap(linha) em cada Map.

4.2. Teste /livros/busca?precoMaximo=25 e confirme que o resultado bate com os livros daquela faixa de preço, agora filtrados pelo próprio banco.

4.3. Confirme que /livros/busca sem nenhum parâmetro (usando o defaultValue já existente) continua trazendo o acervo inteiro.

4.4. Desafio (opcional): crie o método somarValorAcervo() em LivroRepository, executando SELECT SUM(preco) FROM livro com queryForObject(sql, Double.class), e exponha-o na nova rota GET /livros/valor-total.

Por que WHERE preco <= ? é preferível a montar a consulta concatenando precoMaximo diretamente na String? Releia a nota de segurança do material antes de responder.


Entrega individual

Cada integrante da dupla deve, individualmente, no Moodle:

  1. Responder o questionário da semana (perguntas de reflexão sobre a atividade).
  2. Enviar o projeto compactado em .zip (a pasta do projeto sem a subpasta target/, que é gerada na compilação e pesa à toa).