JavaRush /Blog Java /Random-ES /Introducción a Maven, Spring, MySQL, Hibernate y la prime...
Макс
Nivel 41

Introducción a Maven, Spring, MySQL, Hibernate y la primera aplicación CRUD (parte 2)

Publicado en el grupo Random-ES
Buenas tardes. En este artículo me gustaría compartir mi primer encuentro con cosas como Maven, Spring, Hibernate, MySQL y Tomcat en el proceso de creación de una aplicación CRUD simple. Esta es la segunda parte de 4. El artículo está destinado principalmente a aquellos que ya han completado entre 30 y 40 niveles aquí, pero aún no se han aventurado más allá de Java puro y recién están comenzando (o están a punto de comenzar) a ingresar al mundo abierto con todas estas tecnologías, marcos y otras palabras desconocidas. Esta es la segunda parte del artículo "Introducción a Maven, Spring, MySQL, Hibernate y la primera aplicación CRUD". La primera parte se puede ver siguiendo este enlace: Introducción a Maven, Spring, MySQL, Hibernate y la primera aplicación CRUD (parte 1)

Contenido:

Bueno, sigamos adelante, intentemos evocar todo un repositorio de películas. En nuestra pequeña y sencilla aplicación, por supuesto, puede simplemente poner estúpidamente toda la lógica directamente en el controlador, pero, como ya se señaló, es mejor aprender de inmediato a hacer todo correctamente. Por tanto, creemos varias capas. Tendremos un DAO responsable de trabajar con datos, un Servicio , donde habrá todo tipo de lógica, y el Controlador solo procesará solicitudes y llamará a los métodos de servicio necesarios.

Objeto de acceso a datos

El objeto de acceso a datos (DAO) es un patrón de diseño de este tipo. El objetivo es crear una capa especial que será la única responsable de acceder a los datos (trabajando con una base de datos u otro mecanismo de almacenamiento). En el paquete daocrearemos una interfaz FilmDAOen la que habrá métodos como agregar, eliminar, etc. Los llamé un poco diferente, pero corresponden a las operaciones CRUD básicas ( Crear , Leer , Actualizar , Eliminar ).

Vale la pena señalar aquí que, además de DAO, también existe un enfoque como Repositorio, parecen muy similares, ambos se utilizan para trabajar con datos. Todavía no he descubierto qué características tienen estos enfoques y cuál es la diferencia entre ellos. Por lo tanto, puedo estar equivocado aquí y esto debería llamarse repositorio y no Tao, o tal vez sea algo intermedio. Pero en la mayoría de los ejemplos que he visto y estudiado, esto se llama DAO, así que probablemente lo llamaré igual. Al mismo tiempo, quizás en algún lugar más adelante del texto utilice la palabra repositorio. En cualquier caso, si me equivoco en algún punto, perdónenme.

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);
}
Ahora necesitamos su implementación. Todavía no conectaremos la base de datos, todavía da un poco de miedo. Para practicar y acostumbrarnos, primero simularemos el almacenamiento en memoria y crearemos una lista con varias películas. Para almacenar la lista, usaremos not List, pero Map, para que sea conveniente recuperar una película específica por su id, sin tener que revisar toda la lista. Para la generación idusamos AtomicInteger . Creemos una clase FilmDAOImpl, implementemos todos los métodos y completemos el mapa. Algo como eso.
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);
    }
}

Servicio

Ahora agreguemos una capa de servicio. En principio, en este ejemplo es muy posible prescindir de él, limitándonos a DAO, la aplicación será muy sencilla y no está prevista ninguna lógica compleja en el servicio. Pero de repente, en el futuro, querrás agregar todo tipo de complicaciones y cosas interesantes al proyecto, así que, para que esté completo, déjalo así. Por ahora, simplemente llamará a métodos del DAO. serviceCreemos una interfaz en el paquete 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);
}
Y su implementación:
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);
    }
}
La estructura del proyecto ahora se ve así:
Introducción a Maven, Spring, MySQL, Hibernate y la primera aplicación CRUD (parte 2) - 1

Controlador y vistas

Trabajemos ahora en los métodos del controlador y en el llenado de las páginas. Al completar las páginas, necesitaremos algunas técnicas. Por ejemplo, para mostrar una lista de películas, necesitamos un bucle, si, digamos, queremos cambiar alguna inscripción, dependiendo de los parámetros, necesitamos condiciones, etc. El formato JSP (JavaServer Pages) permite utilizar inserciones de código java con las que se puede implementar todo esto. Pero no quiero utilizar código Java mezclado con código HTML en la página. Sería, como mínimo, muy feo. Afortunadamente, para resolver este problema existe algo tan maravilloso como JSTL (Biblioteca de etiquetas estándar de páginas de JavaServer) o la Biblioteca de etiquetas estándar JSP. Nos permite utilizar una gran cantidad de etiquetas adicionales en nuestras páginas JSP para una variedad de necesidades. Conectémoslo a pom.xml:
<dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
</dependency>
Ahora echemos un vistazo al controlador. Primero que nada, eliminemos la creación del objeto desde allí Film, esto se hizo para probar y no necesitamos nada más. Agreguemos un servicio allí y llamemos a sus métodos.
public class FilmController {
    private FilmService filmService = new FilmServiceImpl();
Bueno, en consecuencia crearemos métodos para cada caso, agregaremos, eliminaremos, etc. Primero, un método para mostrar la página principal con una lista de películas:
@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;
    }
No hay nada nuevo aquí. Obtenemos una lista de películas del servicio y la agregamos al modelo. Ahora creemos la página principal films.jspque devuelve este método:
<%@ 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>
Echemos un vistazo más de cerca a esta página para ver qué es qué. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - aquí está conectado el núcleo JSTL, que incluye las etiquetas principales para crear ciclos, condiciones, etc. .
  • <table>— etiqueta para crear una tabla.
  • <tr>- fila de la tabla
  • <th>- encabezado de la columna
  • <td>- celda de la tabla
Primero, creamos una fila de encabezado de tabla con los nombres de las columnas. Luego <c:forEach var="film" items="${filmsList}">, en un bucle (que tomamos del núcleo JSTL), revisamos todos los elementos de la lista pasada ( filmsList), para cada elemento ( film) creamos una nueva fila y escribimos el valor correspondiente en cada celda. Hay un punto aquí: parece que la grabación film.iddebe entenderse como film.getId(), es decir, No se accede directamente al campo, pero se llama al captador. En la última columna ( action) creamos enlaces para eliminar y editar (ahora crearemos los métodos correspondientes). Bueno, a continuación hay un enlace al método para agregar una nueva película. Así es como se ve: A continuación, echemos un vistazo al método que devolverá la página de edición para una película específica:
@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 nuevo ha aparecido aquí: esto es una anotación @PathVariable. Indica que este parámetro ( int id) se obtiene de la barra de direcciones. Para indicar la ubicación de este parámetro en la barra de direcciones, se utiliza la construcción {id}(por cierto, si el nombre de la variable es el mismo, como en este caso, entonces no es necesario indicarlo entre paréntesis, solo escribirlo @PathVariable int id). Entonces, en la página principal hemos creado enlaces para cada película que indican id:
<a href="/edit/${film.id}">edit</a>
Luego, este valor se asigna al parámetro del método y luego lo usamos para obtener una película específica del repositorio a través del servicio y agregarla al modelo. Este fue el método para obtener la página de edición, ahora necesitamos un método para editarla en sí:
@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;
    }
En el método editPageagregamos un atributo al modelo:
modelAndView.addObject("film", filmService.getById(id));
Y ahora con la ayuda de la anotación @ModelAttributeobtenemos este atributo y podemos cambiarlo. Método de solicitud POSTporque aquí pasaremos los datos. " redirect:/" significa que luego de ejecutar este método seremos redirigidos a la dirección " /", es decir El método se ejecutará allFilmsy regresaremos a la página principal. Ahora hagamos la página en sí 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>— un formulario de recogida y envío de datos, indicando quién los tratará ( /edit)
  • <input>— elementos de la interfaz para la interacción del usuario (botones, campos de entrada, etc.)
  • <label>- etiqueta de texto
Entonces, cuando haga clic en el botón, <input type="submit" value="Edit film">los datos del formulario se enviarán al servidor (se ha agregado especialmente un campo invisible con el valor idpara que el servidor sepa qué registro en la base de datos necesita actualizarse). En el método editFilmserán asignados a los campos de atributos correspondientes film. Luego regresaremos a la página principal con una lista actualizada. La página de edición se ve así: Ahora comencemos a agregar nuevas películas a la lista. Para hacer esto, también necesitará un formulario para ingresar y enviar datos. Puede crear un formulario en la página principal o puede crear una página separada, como editPage.jsp. Pero, por otro lado, la forma de añadir será exactamente la misma que de editar, es decir 4 campos de entrada y un botón de enviar. Entonces, ¿por qué crear una nueva página? Usemos editPage.jsp. Método para obtener la página:
@RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView addPage() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("editPage");
        return modelAndView;
    }
En el método, editPageademás pasamos el atributo para cambiarlo más tarde, pero aquí simplemente recibimos la página. Y el método para agregar:
@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 no pasamos el atributo aquí, se creará un nuevo objeto aquí Film. Bueno, básicamente no hay nada nuevo aquí. También vale la pena señalar que tenemos ambos métodos disponibles en " /add". Esto es posible debido a que responden a diferentes tipos de solicitudes. Al seguir el enlace en la página principal, realizamos una solicitud GET, que nos lleva al archivo addPage. Y cuando en la página de agregar hacemos clic en el botón para enviar datos, se realiza una solicitud POST y el archivo addFilm. Para agregar una nueva película, decidimos usar la misma página que para editar. Pero allí los datos se envían a la dirección " /edit":
<c:url value="/edit" var="var"/>
<form action="${var}" method="POST">
    <input type="submit" value="Edit film">
</form>
Necesitamos modificar un poco la página para que se comporte de manera diferente al agregar y editar. Para resolver este problema, usaremos las condiciones de la misma 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>
Aquellos. Sólo estamos revisando el campo film.title. Si está vacío, entonces es una película nueva, debemos completar todos los datos de la misma y agregarla a la lista. Si este campo no está vacío, entonces es una película de la lista y sólo necesitas cambiarla. Eso. obtenemos dos versiones de nuestra página: Bueno, el último método del controlador para eliminar una película de la 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;
    }
Creo que aquí no hace falta comentar nada, todo esto ya está considerado. Ya hemos creado enlaces a esta dirección en la página principal. Bueno, aquí todo parece estar listo, puedes ejecutarlo nuevamente y ver cómo funciona todo.

Repositorio y servicio como componentes Spring

Hagamos una pequeña corrección más. El hecho es que ahora nuestro almacenamiento y servicio son solo clases, y para poder usarlos tenemos que crear un objeto de clase nosotros mismos ( new FilmServiceImpl()). Pero tenemos Spring conectado por una razón , así que déjelo controlar este asunto él mismo. Para poner nuestras clases bajo el control de Spring, debemos indicar que son componentes. Para ello, los marcamos con anotaciones especiales:
@Repository
public class FilmDAOImpl implements FilmDAO {
@Service
public class FilmServiceImpl implements FilmService {
Las anotaciones @Repositoryy @Service, así como también @Controllerse derivan de @Component. Las características y diferencias específicas de estas tres anotaciones y en qué se diferencian de un componente simple deben leerse por separado en la documentación o las guías. Por ahora, basta con saber que estas anotaciones le dicen a Spring que estas clases son un repositorio y un servicio, respectivamente. Y ahora ya no necesitamos crear nosotros mismos objetos concretos de estas clases:
private FilmService filmService = new FilmServiceImpl();
En su lugar, puedes marcar el campo con una anotación especial y Spring seleccionará la implementación adecuada:
@Autowired
private FilmService filmService;
La anotación @Autowired(enlace automático) le dice a Spring que debe profundizar en su contexto y sustituir aquí un bean adecuado. Muy cómodamente. Si antes usábamos interfaces para no preocuparnos por la implementación específica de métodos, ahora ni siquiera necesitamos preocuparnos por la implementación de la interfaz en sí e incluso saber su nombre. La idea es que no se recomienda utilizar el enlace automático en un campo; es mejor utilizar un constructor o definidor. Lea más sobre esto en la documentación. Para nosotros, en principio, esto no es importante, podemos dejarlo así con seguridad. Pero como la idea lo pide, respetaremos que todo sea bonito y sin advertencias amarillas. En la clase de controlador, creemos un definidor y anotémoslo:
@Controller
public class FilmController {

    private FilmService filmService;

    @Autowired
    public void setFilmService(FilmService filmService) {
        this.filmService = filmService;
    }
Y de manera similar hacemos un colocador para FilmDAOen la clase FilmServiceImpl. Continuará... Presentamos Maven, Spring, MySQL, Hibernate y la primera aplicación CRUD (parte 1) Presentamos Maven, Spring, MySQL, Hibernate y la primera aplicación CRUD (parte 2) Presentamos Maven, Spring, MySQL, Hibernate y primera aplicación CRUD (parte 3) Introducción a Maven, Spring, MySQL, Hibernate y la primera aplicación CRUD (parte 4)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION