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:
- Oggetto di accesso ai dati
- Servizio
- Controller e visualizzazioni
- Repository e servizio come componenti Spring
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 pacchettodao
creeremo un'interfaccia FilmDAO
in 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 id
utilizziamo 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.service
Creiamo 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:
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 apom.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.jsp
restituita 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
<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.id
dover 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 editPage
abbiamo aggiunto un attributo al modello:
modelAndView.addObject("film", filmService.getById(id));
E ora con l'aiuto dell'annotazione @ModelAttribute
otteniamo questo attributo e possiamo cambiarlo. Richiedi metodo POST
perché qui passeremo i dati. " redirect:/
" significa che dopo aver eseguito questo metodo verremo reindirizzati all'indirizzo " /
", cioè il metodo verrà eseguito allFilms
e 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
<input type="submit" value="Edit film">
i dati del modulo verranno inviati al server (un campo invisibile con il valore è stato aggiunto appositamente id
in modo che il server sappia quale record nel database deve essere aggiornato). Nel metodo editFilm
verranno 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 editPage
abbiamo 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 @Repository
e @Service
, nonché @Controller
derivano 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 FilmDAO
nella 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)
GO TO FULL VERSION