JavaRush /Blogue Java /Random-PT /Introdução ao Maven, Spring, MySQL, Hibernate e a primeir...
Макс
Nível 41

Introdução ao Maven, Spring, MySQL, Hibernate e a primeira aplicação CRUD (parte 2)

Publicado no grupo Random-PT
Boa tarde. Neste artigo gostaria de compartilhar meu primeiro encontro com coisas como Maven, Spring, Hibernate, MySQL e Tomcat no processo de criação de uma aplicação CRUD simples. Esta é a segunda parte de 4. O artigo é destinado principalmente àqueles que já completaram 30-40 níveis aqui, mas ainda não se aventuraram além do Java puro e estão apenas começando (ou prestes a começar) a entrar no mundo aberto com todas essas tecnologias, frameworks e outras palavras desconhecidas. Esta é a segunda parte do artigo “Introdução ao Maven, Spring, MySQL, Hibernate e a primeira aplicação CRUD”. A primeira parte pode ser vista neste link: Introdução ao Maven, Spring, MySQL, Hibernate e a primeira aplicação CRUD (parte 1)

Contente:

Bem, vamos em frente, vamos agora tentar invocar todo um repositório de filmes. Em nosso aplicativo pequeno e simples, é claro, você pode simplesmente colocar estupidamente toda a lógica no controlador, mas, como já foi dito, é melhor aprender imediatamente como fazer tudo corretamente. Portanto, vamos criar várias camadas. Teremos um DAO responsável por trabalhar com os dados, um Service , onde haverá todo tipo de outra lógica, e o Controller apenas processará as solicitações e chamará os métodos de serviço necessários.

Objeto de acesso a dados

Data Access Object (DAO) é um padrão de design. O objetivo é criar uma camada especial que será a única responsável pelo acesso aos dados (trabalhando com um banco de dados ou outro mecanismo de armazenamento). No pacote daocriaremos uma interface FilmDAOna qual existirão métodos como adicionar, excluir, etc. Eu os chamei de maneira um pouco diferente, mas eles correspondem às operações básicas do CRUD ( C reate, Read , Update , D elete).

É importante destacar aqui que além do DAO, existe também uma abordagem como Repositório, eles parecem ser muito parecidos, ambos são utilizados para trabalhar com dados. Ainda não descobri quais são as características dessas abordagens e qual é a diferença entre elas. Portanto, posso estar enganado aqui e isso deveria ser chamado de repositório, e não de Tao, ou talvez seja algo intermediário. Mas na maioria dos exemplos que vi e estudei, isso se chama DAO, então provavelmente irei chamá-lo da mesma forma. Ao mesmo tempo, talvez em algum lugar mais adiante no texto usarei a palavra repositório. De qualquer forma, se eu estiver errado em algum lugar com isso, por favor, me perdoe.

package testgroup.filmography.dao;

import testgroup.filmography.model.Film;

import java.util.List;

public interface FilmDAO {
    List<Film> allFilms();
    void add(Film film);
    void delete(Film film);
    void edit(Film film);
    Film getById(int id);
}
Agora precisamos de sua implementação. Ainda não vamos conectar o banco de dados, ainda é um pouco assustador. Para praticar e se acostumar, vamos primeiro simular o armazenamento na memória e criar uma lista com vários filmes. Para armazenar a lista, usaremos not List, mas Map, para facilitar a recuperação de um filme específico por seu id, sem passar pela lista inteira. Para geração idusamos AtomicInteger . Vamos criar uma classe FilmDAOImpl, implementar todos os métodos e preencher o mapa. Algo parecido.
package testgroup.filmography.dao;

import testgroup.filmography.model.Film;

import java.util.*;

public class FilmDAOImpl implements FilmDAO {
    private static final AtomicInteger AUTO_ID = new AtomicInteger(0);
    private static Map<Integer, Film> films = new HashMap<>();

    static {
        Film film1 = new Film();
        film1.setId(AUTO_ID.getAndIncrement());
        film1.setTitle("Inception");
        film1.setYear(2010);
        film1.setGenre("sci-fi");
        film1.setWatched(true);
        films.put(film1.getId(), film1);

        // + film2, film3, film4, ...
    }
    @Override
    public List<Film> allFilms() {
        return new ArrayList<>(films.values());
    }

    @Override
    public void add(Film film) {
        film.setId(AUTO_ID.getAndIncrement());
        films.put(film.getId(), film);
    }

    @Override
    public void delete(Film film) {
        films.remove(film.getId());
    }

    @Override
    public void edit(Film film) {
        films.put(film.getId(), film);
    }

    @Override
    public Film getById(int id) {
        return films.get(id);
    }
}

Serviço

Agora vamos adicionar uma camada de serviço. Em princípio, neste exemplo é perfeitamente possível prescindir dele, limitando-nos ao DAO; a aplicação será muito simples e não há planos para nenhuma lógica complexa no serviço. Mas de repente, no futuro, você desejará adicionar todos os tipos de complicações e coisas interessantes ao projeto, então, para completar, deixe como está. Por enquanto, ele simplesmente chamará métodos do DAO. serviceVamos criar uma interface no pacote FilmService.
package testgroup.filmography.service;

import testgroup.filmography.model.Film;

import java.util.List;

public interface FilmService {
    List<Film> allFilms();
    void add(Film film);
    void delete(Film film);
    void edit(Film film);
    Film getById(int id);
}
E sua implementação:
package testgroup.filmography.service;

import testgroup.filmography.dao.FilmDAO;
import testgroup.filmography.dao.FilmDAOImpl;
import testgroup.filmography.model.Film;

import java.util.List;

public class FilmServiceImpl implements FilmService {
    private FilmDAO filmDAO = new FilmDAOImpl();

    @Override
    public List<Film> allFilms() {
        return filmDAO.allFilms();
    }

    @Override
    public void add(Film film) {
        filmDAO.add(film);
    }

    @Override
    public void delete(Film film) {
        filmDAO.delete(film);
    }

    @Override
    public void edit(Film film) {
        filmDAO.edit(film);
    }

    @Override
    public Film getById(int id) {
        return filmDAO.getById(id);
    }
}
A estrutura do projeto agora fica assim:
Introdução ao Maven, Spring, MySQL, Hibernate e a primeira aplicação CRUD (parte 2) - 1

Controlador e visualizações

Vamos agora trabalhar nos métodos do controlador e no preenchimento das páginas. Na hora de preencher as páginas precisaremos de algumas técnicas. Por exemplo, para exibir uma lista de filmes, precisamos de um loop, se, digamos, quisermos alterar alguma inscrição, dependendo dos parâmetros, precisamos de condições, etc. O formato JSP (JavaServer Pages) permite usar inserções de código Java com as quais tudo isso pode ser implementado. Mas não quero usar código Java misturado com código HTML na página. Seria, no mínimo, muito feio. Felizmente, para resolver esse problema existe algo maravilhoso como JSTL (JavaServer Pages Standard Tag Library) ou JSP Standard Tag Library. Isso nos permite usar um monte de tags adicionais em nossas páginas JSP para diversas necessidades. Vamos conectá-lo a pom.xml:
<dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
</dependency>
Agora vamos dar uma olhada no controlador. Primeiramente vamos retirar daí a criação do objeto Film, isso foi feito para teste e não precisamos de mais nada. Vamos adicionar um serviço lá e chamar seus métodos.
public class FilmController {
    private FilmService filmService = new FilmServiceImpl();
Bom, nesse sentido iremos criar métodos para cada caso, adicionar, deletar, etc. Primeiro, um método para exibir a página principal com uma lista de filmes:
@RequestMapping(method = RequestMethod.GET)
    public ModelAndView allFilms() {
        List<Film> films = filmService.allFilms();
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("films");
        modelAndView.addObject("filmsList", films);
        return modelAndView;
    }
Não há nada de novo aqui. Obtemos uma lista de filmes do serviço e adicionamos ao modelo. Agora vamos fazer a página principal films.jspque esse método retorna:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>FILMS</title>
</head>
<body>

<h2>Films</h2>
<table>
    <tr>
        <th>id</th>
        <th>title</th>
        <th>year</th>
        <th>genre</th>
        <th>watched</th>
        <th>action</th>
    </tr>
    <c:forEach var="film" items="${filmsList}">
        <tr>
            <td>${film.id}</td>
            <td>${film.title}</td>
            <td>${film.year}</td>
            <td>${film.genre}</td>
            <td>${film.watched}</td>
            <td>
                <a href="/edit/${film.id}">edit</a>
                <a href="/delete/${film.id}">delete</a>
            </td>
        </tr>
    </c:forEach>
</table>

<h2>Add</h2>
<c:url value="/add" var="add"/>
<a href="${add}">Add new film</a>
</body>
</html>
Vamos dar uma olhada mais de perto nesta página para ver o que é. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - aqui está conectado o núcleo JSTL, que inclui as principais tags para criação de ciclos, condições, etc. .
  • <table>— tag para criar uma tabela.
  • <tr>- linha da tabela
  • <th>- cabeçalho da coluna
  • <td>- célula da tabela
Primeiro, criamos uma linha de cabeçalho da tabela com os nomes das colunas. Então <c:forEach var="film" items="${filmsList}">, em um loop (que pegamos do núcleo JSTL), percorremos todos os elementos da lista passada ( filmsList), para cada elemento ( film) criamos uma nova linha e escrevemos o valor correspondente em cada célula. Há um ponto aqui: a gravação parece film.idprecisar ser entendida como film.getId(), ou seja, O campo não é acessado diretamente, mas o getter é chamado. Na última coluna ( action) fazemos links para exclusão e edição (criaremos agora os métodos correspondentes). Bem, abaixo está um link para o método para adicionar um novo filme. Esta é a aparência: A seguir, vamos dar uma olhada no método que retornará a página de edição de um filme específico:
@RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
    public ModelAndView editPage(@PathVariable("id") int id) {
        Film film = filmService.getById(id);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("editPage");
        modelAndView.addObject("film", film);
        return modelAndView;
    }
Algo novo apareceu aqui - esta é uma anotação @PathVariable. Indica que este parâmetro ( int id) é obtido na barra de endereço. Para indicar a localização deste parâmetro na barra de endereço, utiliza-se a construção {id}(aliás, se o nome da variável for o mesmo, como neste caso, então não é necessário indicá-lo entre parênteses, apenas escrevê-lo @PathVariable int id). Assim, na página principal fizemos links para cada filme indicando id:
<a href="/edit/${film.id}">edit</a>
Em seguida, esse valor é atribuído ao parâmetro do método e então o usamos para obter um filme específico do repositório por meio do serviço e adicioná-lo ao modelo. Este foi o método para obter a página de edição, agora precisamos de um método para editar em si:
@RequestMapping(value = "/edit", method = RequestMethod.POST)
    public ModelAndView editFilm(@ModelAttribute("film") Film film) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("redirect:/");
        filmService.edit(film);
        return modelAndView;
    }
No método editPageadicionamos um atributo ao modelo:
modelAndView.addObject("film", filmService.getById(id));
E agora com a ajuda da anotação @ModelAttributeobtemos esse atributo e podemos alterá-lo. Método de solicitação POSTporque aqui passaremos os dados. " redirect:/" significa que após executar este método seremos redirecionados para o endereço " /", ou seja, o método será executado allFilmse retornaremos à página principal. Agora vamos fazer a própria página editPage.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Edit</title>
</head>
<body>
<c:url value="/edit" var="var"/>
<form action="${var}" method="POST">
    <input type="hidden" name="id" value="${film.id}">
    <label for="title">Title</label>
    <input type="text" name="title" id="title">
    <label for="year">Year</label>
    <input type="text" name="year" id="year">
    <label for="genre">Genre</label>
    <input type="text" name="genre" id="genre">
    <label for="watched">Watched</label>
    <input type="text" name="watched" id="watched">
    <input type="submit" value="Edit film">
</form>
</body>
</html>
  • <form>— um formulário para coleta e envio de dados, indicando quem irá processá-los ( /edit)
  • <input>— elementos de interface para interação do usuário (botões, campos de entrada, etc.)
  • <label>- rótulo de texto
Assim, ao clicar no botão, <input type="submit" value="Edit film">os dados do formulário serão enviados para o servidor (um campo invisível com o valor foi adicionado especialmente idpara que o servidor saiba qual registro do banco de dados precisa ser atualizado). No método editFilmeles serão atribuídos aos campos de atributos correspondentes film. Voltaremos então à página principal com uma lista atualizada. A página de edição fica assim: Agora vamos começar a adicionar novos filmes à lista. Para isso, você também precisará de um formulário para inserção e envio de dados. Você pode criar um formulário na página principal ou uma página separada, como editPage.jsp. Mas, por outro lado, a forma de adição será exatamente a mesma da edição, ou seja, 4 campos de entrada e um botão enviar. Então, por que criar uma nova página, vamos usar editPage.jsp. Método para obter a página:
@RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView addPage() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("editPage");
        return modelAndView;
    }
No método, editPagepassamos adicionalmente o atributo para alterá-lo posteriormente, mas aqui simplesmente recebemos a página. E o método para adicionar:
@RequestMapping(value = "/add", method = RequestMethod.POST)
    public ModelAndView addFilm(@ModelAttribute("film") Film film) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("redirect:/");
        filmService.add(film);
        return modelAndView;
    }
Como não passamos o atributo aqui, um novo objeto será criado aqui Film. Bem, basicamente não há nada de novo aqui. Vale destacar também que temos ambos os métodos disponíveis em " /add". Isso é possível pelo fato de responderem a diversos tipos de solicitações. Seguindo o link da página principal fazemos uma solicitação GET, que nos leva ao arquivo addPage. E quando na página de adição clicamos no botão para enviar dados, é feita uma solicitação POST e o arquivo addFilm. Para adicionar um novo filme, decidimos usar a mesma página de edição. Mas aí os dados são enviados para o endereço " /edit":
<c:url value="/edit" var="var"/>
<form action="${var}" method="POST">
    <input type="submit" value="Edit film">
</form>
Precisamos ajustar um pouco a página para que ela se comporte de maneira diferente para adição e edição. Para resolver esse problema, usaremos as condições da mesma biblioteca principal JSTL:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <c:if test="${empty film.title}">
        <title>Add</title>
    </c:if>
    <c:if test="${!empty film.title}">
        <title>Edit</title>
    </c:if>
</head>
<body>
<c:if test="${empty film.title}">
    <c:url value="/add" var="var"/>
</c:if>
<c:if test="${!empty film.title}">
    <c:url value="/edit" var="var"/>
</c:if>
<form action="${var}" method="POST">
    <c:if test="${!empty film.title}">
        <input type="hidden" name="id" value="${film.id}">
    </c:if>
    <label for="title">Title</label>
    <input type="text" name="title" id="title">
    <label for="year">Year</label>
    <input type="text" name="year" id="year">
    <label for="genre">Genre</label>
    <input type="text" name="genre" id="genre">
    <label for="watched">Watched</label>
    <input type="text" name="watched" id="watched">
    <c:if test="${empty film.title}">
        <input type="submit" value="Add new film">
    </c:if>
    <c:if test="${!empty film.title}">
        <input type="submit" value="Edit film">
    </c:if>
</form>
</body>
</html>
Aqueles. estamos apenas verificando o campo film.title. Se estiver vazio, então é um filme novo, devemos preencher todos os dados dele e adicioná-lo à lista. Se este campo não estiver vazio, então é um filme da lista e basta alterá-lo. Que. temos duas versões de nossa página: Bem, o último método do controlador para remover um filme da lista:
@RequestMapping(value="/delete/{id}", method = RequestMethod.GET)
    public ModelAndView deleteFilm(@PathVariable("id") int id) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("redirect:/");
        Film film = filmService.getById(id);
        filmService.delete(film);
        return modelAndView;
    }
Acho que não há necessidade de comentar nada aqui, tudo isso já foi considerado. Já criamos links para este endereço na página principal. Bom, parece que tudo está pronto aqui, você pode rodar novamente e ver como tudo funciona.

Repositório e serviço como componentes Spring

Vamos fazer mais uma pequena correção. O fato é que agora nosso armazenamento e serviço são apenas classes, e para utilizá-los temos que criar nós mesmos um objeto de classe ( new FilmServiceImpl()). Mas temos Spring conectado por um motivo , então deixe-o controlar esse assunto sozinho. Para colocar nossas classes sob o controle do Spring, precisamos indicar que elas são componentes. Para fazer isso, marcamos-os com anotações especiais:
@Repository
public class FilmDAOImpl implements FilmDAO {
@Service
public class FilmServiceImpl implements FilmService {
As anotações @Repositorye @Service, bem como @Controllersão derivadas de @Component. Quais são as características específicas e diferenças dessas três anotações e como elas diferem de um componente simples devem ser lidas separadamente na documentação ou nos guias. Por enquanto, basta saber que essas anotações informam ao Spring que essas classes são um repositório e um serviço, respectivamente. E agora não precisamos mais criar nós mesmos objetos concretos dessas classes:
private FilmService filmService = new FilmServiceImpl();
Em vez disso, você pode marcar o campo com uma anotação especial e o Spring selecionará a implementação apropriada:
@Autowired
private FilmService filmService;
A anotação @Autowired(ligação automática) diz ao Spring que ele deve se aprofundar em seu contexto e substituir um bean adequado aqui. Muito confortavelmente. Se antes usávamos interfaces para não nos preocuparmos com a implementação específica dos métodos, agora nem precisamos nos preocupar com a implementação da interface em si e nem saber o seu nome. A ideia é que não é recomendado usar auto-binding em um campo; é melhor usar um construtor ou setter. Leia mais sobre isso na documentação. Para nós, em princípio, isso não é importante, podemos deixar assim com segurança. Mas, como a ideia pede, vamos respeitar que tudo esteja lindo e sem avisos amarelos. Na classe controlador, vamos criar um setter e anotá-lo:
@Controller
public class FilmController {

    private FilmService filmService;

    @Autowired
    public void setFilmService(FilmService filmService) {
        this.filmService = filmService;
    }
E da mesma forma fazemos um setter FilmDAOna classe FilmServiceImpl. Continua... Apresentando Maven, Spring, MySQL, Hibernate e o primeiro aplicativo CRUD (parte 1) Apresentando Maven, Spring, MySQL, Hibernate e o primeiro aplicativo CRUD (parte 2) Apresentando Maven, Spring, MySQL, Hibernate e o primeira aplicação CRUD (parte 3) Introdução ao Maven, Spring, MySQL, Hibernate e a primeira aplicação CRUD (parte 4)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION