JavaRush /Java Blog /Random-IT /Introduzione a Maven, Spring, MySQL, Hibernate e alla pri...
Макс
Livello 41

Introduzione a Maven, Spring, MySQL, Hibernate e alla prima applicazione CRUD (parte 2)

Pubblicato nel gruppo Random-IT
Buon pomeriggio. In questo articolo vorrei condividere il mio primo incontro con cose come Maven, Spring, Hibernate, MySQL e Tomcat nel processo di creazione di una semplice applicazione CRUD. Questa è la seconda parte di 4. L'articolo è destinato principalmente a coloro che hanno già completato 30-40 livelli qui, ma non si sono ancora avventurati oltre Java puro e stanno appena iniziando (o stanno per iniziare) ad entrare nel mondo aperto con tutte queste tecnologie, strutture e altre parole sconosciute. Questa è la seconda parte dell'articolo "Introduzione a Maven, Spring, MySQL, Hibernate e alla prima applicazione CRUD". La prima parte può essere vista seguendo questo link: Introduzione a Maven, Spring, MySQL, Hibernate e alla prima applicazione CRUD (parte 1)

Contenuto:

Bene, andiamo avanti, proviamo ora a evocare un intero archivio di film. Nella nostra piccola e semplice applicazione, ovviamente, puoi semplicemente stupidamente inserire tutta la logica nel controller, ma, come già notato, è meglio imparare subito come fare tutto correttamente. Pertanto, creiamo diversi livelli. Avremo un DAO responsabile dell'utilizzo dei dati, un servizio , dove ci saranno tutti i tipi di altre logiche e il controller elaborerà solo le richieste e chiamerà i metodi di servizio necessari.

Oggetto di accesso ai dati

DAO (Data Access Object) è un modello di progettazione di questo tipo. Il punto è creare un livello speciale che sarà l'unico responsabile dell'accesso ai dati (lavorando con un database o un altro meccanismo di archiviazione). Nel pacchetto daocreeremo un'interfaccia FilmDAOin cui saranno presenti metodi come aggiungi, elimina, ecc. Li ho chiamati in modo leggermente diverso, ma corrispondono alle operazioni CRUD di base ( C reate, Read , Update , D elete).

Vale la pena notare qui che oltre a DAO esiste anche un approccio come Repository, sembrano essere molto simili, entrambi sono utilizzati per lavorare con i dati. Non ho ancora capito quali caratteristiche abbiano questi approcci e quale sia la differenza tra loro. Pertanto, potrei sbagliarmi qui e questo dovrebbe essere chiamato un deposito, e non un Tao, o forse è qualcosa nel mezzo. Ma nella maggior parte degli esempi che ho visto e studiato, questo si chiama DAO, quindi probabilmente lo chiamerò allo stesso modo. Allo stesso tempo, forse da qualche parte più avanti nel testo userò la parola repository. In ogni caso, se sbaglio qualcosa in questo, per favore perdonatemi.

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);
}
Ora abbiamo bisogno della sua attuazione. Non collegheremo ancora il database, fa ancora un po’ paura. Per esercitarci e abituarci, simuliamo prima la memorizzazione in memoria e creiamo un elenco con più film. Per memorizzare l'elenco, utilizzeremo non List, ma Map, per rendere conveniente il recupero di un film specifico tramite il suo id, senza scorrere l'intero elenco. Per la generazione idutilizziamo AtomicInteger . Creiamo una classe FilmDAOImpl, implementiamo tutti i metodi e compiliamo la mappa. Qualcosa del genere.
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);
    }
}

Servizio

Ora aggiungiamo un livello di servizio. In linea di principio, in questo esempio è del tutto possibile farne a meno, limitandosi a DAO, l'applicazione sarà molto semplice e non sono previste logiche complesse nel servizio. Ma all'improvviso in futuro vorrai aggiungere ogni sorta di complicazioni e cose interessanti al progetto, quindi per completezza, lascia che sia. Per ora, chiamerà semplicemente i metodi dal DAO. serviceCreiamo un'interfaccia nel pacchetto 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 la sua implementazione:
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 struttura del progetto ora assomiglia a questa:
Introduzione a Maven, Spring, MySQL, Hibernate e alla prima applicazione CRUD (parte 2) - 1

Controller e visualizzazioni

Lavoriamo ora sui metodi del controller e sul riempimento delle pagine. Durante la compilazione delle pagine, avremo bisogno di alcune tecniche. Ad esempio, per visualizzare un elenco di film, abbiamo bisogno di un ciclo, se, diciamo, vogliamo cambiare qualche iscrizione, a seconda dei parametri, abbiamo bisogno di condizioni, ecc. Il formato JSP (JavaServer Pages) consente di utilizzare inserimenti di codice Java con cui è possibile implementare tutto ciò. Ma non voglio utilizzare il codice Java mescolato con il codice HTML nella pagina. Sarebbe, come minimo, molto brutto. Fortunatamente, per risolvere questo problema esiste una cosa meravigliosa come JSTL (JavaServer Pages Standard Tag Library) o JSP Standard Tag Library. Ci consente di utilizzare tutta una serie di tag aggiuntivi nelle nostre pagine JSP per una varietà di esigenze. Colleghiamolo a pom.xml:
<dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
</dependency>
Ora diamo un'occhiata al controller. Innanzitutto togliamo da lì la creazione dell’oggetto Film, questo è stato fatto per testare e non ci serve altro. Aggiungiamo un servizio lì e chiamiamo i suoi metodi.
public class FilmController {
    private FilmService filmService = new FilmServiceImpl();
Bene, di conseguenza creeremo metodi per ciascun caso, aggiungeremo, elimineremo, ecc. Innanzitutto un metodo per visualizzare la pagina principale con un elenco di film:
@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;
    }
Non c'è niente di nuovo qui. Otteniamo un elenco di film dal servizio e lo aggiungiamo al modello. Ora creiamo la pagina principale films.jsprestituita da questo metodo:
<%@ 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>
Diamo un'occhiata più da vicino a questa pagina per vedere di cosa si tratta. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - qui è collegato il core JSTL, che include i tag principali per la creazione di cicli, condizioni, ecc. .
  • <table>— tag per creare una tabella.
  • <tr>— riga della tabella
  • <th>- intestazione di colonna
  • <td>— cella della tabella
Per prima cosa creiamo una riga di intestazione della tabella con i nomi delle colonne. Quindi <c:forEach var="film" items="${filmsList}">, in un ciclo (che abbiamo preso dal core JSTL), esaminiamo tutti gli elementi della lista passata ( filmsList), per ogni elemento ( film) creiamo una nuova riga e scriviamo il valore corrispondente in ogni cella. C'è un punto qui, la registrazione sembra film.iddover essere intesa come film.getId(), cioè Non si accede direttamente al campo, ma viene chiamato il getter. Nell'ultima colonna ( action) creiamo i collegamenti per l'eliminazione e la modifica (creeremo ora i metodi corrispondenti). Bene, di seguito è riportato un collegamento al metodo per aggiungere un nuovo film. Ecco come appare: Successivamente, diamo un'occhiata al metodo che restituirà la pagina di modifica per un film specifico:
@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;
    }
Qui è apparso qualcosa di nuovo: questa è un'annotazione @PathVariable. Indica che questo parametro ( int id) è ottenuto dalla barra degli indirizzi. Per indicare la posizione di questo parametro nella barra degli indirizzi, viene utilizzata la costruzione {id}(a proposito, se il nome della variabile è lo stesso, come in questo caso, non è necessario indicarlo tra parentesi, ma basta scriverlo @PathVariable int id). Quindi, nella pagina principale abbiamo creato collegamenti per ciascun film indicando id:
<a href="/edit/${film.id}">edit</a>
Quindi questo valore viene assegnato al parametro del metodo e quindi lo utilizziamo per ottenere un film specifico dal repository tramite il servizio e aggiungerlo al modello. Questo era il metodo per ottenere la pagina di modifica, ora abbiamo bisogno di un metodo per modificare stessa:
@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;
    }
Nel metodo editPageabbiamo aggiunto un attributo al modello:
modelAndView.addObject("film", filmService.getById(id));
E ora con l'aiuto dell'annotazione @ModelAttributeotteniamo questo attributo e possiamo cambiarlo. Richiedi metodo POSTperché qui passeremo i dati. " redirect:/" significa che dopo aver eseguito questo metodo verremo reindirizzati all'indirizzo " /", cioè il metodo verrà eseguito allFilmse torneremo alla pagina principale. Ora creiamo la pagina stessa 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 modulo per la raccolta e l'invio dei dati, con l'indicazione di chi li tratterà ( /edit)
  • <input>— elementi dell'interfaccia per l'interazione dell'utente (pulsanti, campi di input, ecc.)
  • <label>- etichetta di testo
Pertanto, quando si fa clic sul pulsante, <input type="submit" value="Edit film">i dati del modulo verranno inviati al server (un campo invisibile con il valore è stato aggiunto appositamente idin modo che il server sappia quale record nel database deve essere aggiornato). Nel metodo editFilmverranno assegnati ai campi attributo corrispondenti film. Torneremo poi alla pagina principale con l'elenco aggiornato. La pagina di modifica si presenta così: Ora iniziamo ad aggiungere nuovi film all'elenco. Per fare ciò, avrai bisogno anche di un modulo per l'inserimento e l'invio dei dati. Puoi creare un modulo nella pagina principale oppure puoi creare una pagina separata, come editPage.jsp. Ma, d'altra parte, il modulo per l'aggiunta sarà esattamente lo stesso di quello per la modifica, ad es. 4 campi di input e un pulsante di invio. Allora perché creare una nuova pagina, usiamo editPage.jsp. Metodo per ottenere la pagina:
@RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView addPage() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("editPage");
        return modelAndView;
    }
Nel metodo editPageabbiamo inoltre passato l'attributo per poterlo modificare in seguito, ma qui riceviamo semplicemente la pagina. E il metodo per aggiungere:
@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;
    }
Poiché non abbiamo passato l'attributo qui, verrà creato un nuovo oggetto qui Film. Bene, non c’è praticamente nulla di nuovo qui. Vale anche la pena notare che entrambi i metodi sono disponibili su " /add". Ciò è possibile perché rispondono a diversi tipi di richieste. Seguendo il collegamento nella pagina principale effettuiamo una richiesta GET, che ci porta al file addPage. E quando nella pagina di aggiunta facciamo clic sul pulsante per inviare dati, viene effettuata una richiesta POST e il file addFilm. Per aggiungere un nuovo film, abbiamo deciso di utilizzare la stessa pagina della modifica. Ma lì i dati vengono inviati all'indirizzo " /edit":
<c:url value="/edit" var="var"/>
<form action="${var}" method="POST">
    <input type="submit" value="Edit film">
</form>
Dobbiamo modificare leggermente la pagina in modo che si comporti diversamente durante l'aggiunta e la modifica. Per risolvere questo problema, utilizzeremo le condizioni della stessa libreria principale 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>
Quelli. stiamo solo controllando il campo film.title. Se è vuoto, allora è un nuovo film, dobbiamo inserire tutti i dati e aggiungerlo all'elenco. Se questo campo non è vuoto, allora è un film dall'elenco e devi solo cambiarlo. Quello. otteniamo due versioni della nostra pagina: Bene, l'ultimo metodo del controller per rimuovere un film dall'elenco:
@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;
    }
Penso che non sia necessario commentare nulla qui, tutto questo è già stato considerato. Abbiamo già creato collegamenti a questo indirizzo nella pagina principale. Bene, qui sembra che tutto sia pronto, puoi eseguirlo di nuovo e vedere come funziona il tutto.

Repository e servizio come componenti Spring

Facciamo un'altra piccola correzione. Il fatto è che ora il nostro archivio e il nostro servizio sono solo classi e per usarli dobbiamo creare noi stessi un oggetto classe ( new FilmServiceImpl()). Ma abbiamo Spring connesso per un motivo , quindi lascia che sia lui a controllare la questione da solo. Per mettere le nostre classi sotto il controllo di Spring, dobbiamo indicare che sono componenti. Per fare ciò, li contrassegniamo con annotazioni speciali:
@Repository
public class FilmDAOImpl implements FilmDAO {
@Service
public class FilmServiceImpl implements FilmService {
Le annotazioni @Repositorye @Service, nonché @Controllerderivano da @Component. Quali sono le caratteristiche specifiche e le differenze di queste tre annotazioni e come differiscono da un componente semplice dovrebbero essere lette separatamente nella documentazione o nelle guide. Per ora è sufficiente sapere che queste annotazioni dicono a Spring che queste classi sono rispettivamente un repository e un servizio. E ora non abbiamo più bisogno di creare noi stessi oggetti concreti di queste classi:
private FilmService filmService = new FilmServiceImpl();
Puoi invece contrassegnare il campo con un'annotazione speciale e Spring selezionerà l'implementazione appropriata:
@Autowired
private FilmService filmService;
L'annotazione @Autowired(associazione automatica) dice a Spring che dovrebbe scavare nel suo contesto e sostituire qui un bean adatto. Molto comodamente. Se prima utilizzavamo le interfacce per non preoccuparci dell'implementazione specifica dei metodi, ora non dobbiamo nemmeno preoccuparci dell'implementazione dell'interfaccia stessa e nemmeno conoscerne il nome. L'idea è che non è consigliabile utilizzare l'associazione automatica su un campo; è meglio utilizzare un costruttore o un setter. Maggiori informazioni a riguardo nella documentazione. Per noi, in linea di principio, questo non è importante, possiamo tranquillamente lasciarlo così. Ma, poiché l'idea lo richiede, rispetteremo che tutto sia bello e senza avvisi gialli. Nella classe controller, creiamo un setter e annotiamolo:
@Controller
public class FilmController {

    private FilmService filmService;

    @Autowired
    public void setFilmService(FilmService filmService) {
        this.filmService = filmService;
    }
E allo stesso modo creiamo un setter FilmDAOnella classe FilmServiceImpl. Continua... Presentazione di Maven, Spring, MySQL, Hibernate e la prima applicazione CRUD (parte 1) Presentazione di Maven, Spring, MySQL, Hibernate e la prima applicazione CRUD (parte 2) Presentazione di Maven, Spring, MySQL, Hibernate e la prima applicazione CRUD (parte 3) Introduzione a Maven, Spring, MySQL, Hibernate e alla prima applicazione CRUD (parte 4)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION