JavaRush /Blog Java /Random-PL /Wprowadzenie do Mavena, Springa, MySQL, Hibernate i pierw...
Макс
Poziom 41

Wprowadzenie do Mavena, Springa, MySQL, Hibernate i pierwszej aplikacji CRUD (część 2)

Opublikowano w grupie Random-PL
Dzień dobry. W tym artykule chciałbym podzielić się swoim pierwszym spotkaniem z takimi rzeczami jak Maven, Spring, Hibernate, MySQL i Tomcat w procesie tworzenia prostej aplikacji CRUD. To jest druga część 4. Artykuł przeznaczony jest przede wszystkim dla tych, którzy przeszli już tutaj 30-40 poziomów, ale jeszcze nie odważyli się wyjść poza czystą Javę i dopiero zaczynają (lub dopiero zaczynają) wkraczać w otwarty świat z wszystkie te technologie, frameworki i inne nieznane słowa. To druga część artykułu „Wprowadzenie do Mavena, Springa, MySQL, Hibernate i pierwszej aplikacji CRUD”. Pierwszą część można zobaczyć pod tym linkiem: Wprowadzenie do Mavena, Springa, MySQL, Hibernate i pierwszej aplikacji CRUD (część 1)

Treść:

No cóż, przejdźmy dalej, spróbujmy teraz wyczarować całe repozytorium filmów. W naszej małej i prostej aplikacji możesz oczywiście po prostu głupio umieścić całą logikę bezpośrednio w kontrolerze, ale, jak już wspomniano, lepiej od razu nauczyć się, jak zrobić wszystko poprawnie. Dlatego utwórzmy kilka warstw. Będziemy mieli DAO odpowiedzialnego za pracę z danymi, Usługę , w której będzie cała gama innej logiki, a Administrator będzie jedynie przetwarzał żądania i wywoływał niezbędne metody serwisowe.

Obiekt dostępu do danych

Takim wzorcem projektowym jest obiekt dostępu do danych (DAO) . Chodzi o to, aby utworzyć specjalną warstwę, która będzie wyłącznie odpowiedzialna za dostęp do danych (praca z bazą danych lub innym mechanizmem przechowywania). W pakiecie daostworzymy interfejs, FilmDAOw którym znajdą się metody takie jak dodawanie, usuwanie itp. Nazwałem je trochę inaczej, ale odpowiadają one podstawowym operacjom CRUD ( C reate, Read , U pdate, D elete).

Warto tutaj zaznaczyć, że oprócz DAO istnieje również takie podejście jak Repozytorium, wydają się być bardzo podobne, oba służą do pracy z danymi. Nie wiem jeszcze, jakie cechy mają te podejścia i jaka jest między nimi różnica. Dlatego mogę się tu mylić i należy to nazwać repozytorium, a nie Tao, a może czymś pomiędzy. Ale w większości przykładów, które widziałem i przestudiowałem, nazywa się to DAO, więc prawdopodobnie będę to nazywał tak samo. Jednocześnie być może w dalszej części tekstu użyję słowa repozytorium. W każdym razie, jeśli gdzieś się mylę, proszę o wybaczenie.

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);
}
Teraz potrzebujemy jego wdrożenia. Nie będziemy jeszcze podłączać bazy danych, to wciąż trochę przerażające. Aby to przećwiczyć i przyzwyczaić się, najpierw zasymulujmy przechowywanie w pamięci i utwórz listę z kilkoma filmami. Do przechowywania listy użyjemy not List, ale Map, aby wygodnie było pobrać konkretny film po jego id, bez konieczności przeglądania całej listy. Do generowania idużywamy AtomicInteger . Stwórzmy klasę FilmDAOImpl, zaimplementujmy wszystkie metody i wypełnijmy mapę. Coś w tym stylu.
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);
    }
}

Praca

Dodajmy teraz warstwę usług. W zasadzie w tym przykładzie można się bez tego obejść, ograniczając się do DAO, aplikacja będzie bardzo prosta i nie ma w planach żadnej skomplikowanej logiki w usłudze. Ale nagle w przyszłości chcesz dodać do projektu wszelkiego rodzaju komplikacje i ciekawe rzeczy, więc dla kompletności niech tak będzie. Na razie będzie po prostu wywoływać metody z DAO. serviceStwórzmy interfejs w pakiecie 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);
}
I jego realizacja:
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);
    }
}
Struktura projektu wygląda teraz tak:
Wprowadzenie do Mavena, Springa, MySQL, Hibernate i pierwszej aplikacji CRUD (część 2) - 1

Kontroler i widoki

Popracujmy teraz nad metodami kontrolera i wypełnianiem stron. Podczas wypełniania stron będziemy potrzebować pewnych technik. Np. żeby wyświetlić listę filmów potrzebujemy pętli, jeżeli chcemy zmienić jakiś napis w zależności od parametrów, potrzebujemy warunków itp. Format JSP (JavaServer Pages) umożliwia użycie wstawek kodu Java, za pomocą których można to wszystko zaimplementować. Ale nie chcę używać na stronie kodu Java zmieszanego z kodem HTML. Byłoby to co najmniej bardzo brzydkie. Na szczęście, aby rozwiązać ten problem, istnieje coś takiego jak JSTL (JavaServer Pages Standard Tag Library) lub standardowa biblioteka tagów JSP. Pozwala nam to używać całej gamy dodatkowych tagów na naszych stronach JSP dla różnych potrzeb. Podłączmy to do pom.xml:
<dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
</dependency>
Przyjrzyjmy się teraz kontrolerowi. Przede wszystkim usuńmy stamtąd tworzenie obiektu Film, zostało to zrobione w celach testowych i niczego więcej nie potrzebujemy. Dodajmy tam usługę i wywołajmy jej metody.
public class FilmController {
    private FilmService filmService = new FilmServiceImpl();
Cóż, odpowiednio stworzymy metody dla każdego przypadku, dodamy, usuniemy itp. Najpierw sposób na wyświetlenie strony głównej z listą filmów:
@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;
    }
Nie ma tu nic nowego. Pobieramy listę filmów z serwisu i dodajemy ją do modelu. Stwórzmy teraz stronę główną films.jspzwracaną przez tę metodę:
<%@ 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>
Przyjrzyjmy się bliżej tej stronie, aby zobaczyć, co jest co. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - tutaj podłączony jest rdzeń JSTL, który zawiera główne tagi do tworzenia cykli, warunków itp. .
  • <table>— znacznik do tworzenia tabeli.
  • <tr>— rząd tabeli
  • <th>- nagłówek kolumny
  • <td>— komórka tabeli
Najpierw tworzymy wiersz nagłówka tabeli z nazwami kolumn. Następnie <c:forEach var="film" items="${filmsList}">w pętli (którą wzięliśmy z rdzenia JSTL) przeglądamy wszystkie elementy przekazanej listy ( filmsList), dla każdego elementu ( film) tworzymy nowy wiersz i do każdej komórki wpisujemy odpowiednią wartość. Jest tu jedno, nagranie wydaje się film.idbyć rozumiane jako film.getId(), tj. Dostęp do pola nie jest możliwy bezpośrednio, ale wywoływany jest moduł pobierający. W ostatniej kolumnie ( action) tworzymy linki do usuwania i edycji (teraz utworzymy odpowiednie metody). Cóż, poniżej znajduje się link do metody dodawania nowego filmu. Oto jak to wygląda: Następnie przyjrzyjmy się metodzie, która zwróci stronę edycji konkretnego filmu:
@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;
    }
Pojawiło się tu coś nowego - to jest adnotacja @PathVariable. Wskazuje, że ten parametr ( int id) jest pobierany z paska adresu. Aby wskazać lokalizację tego parametru w pasku adresu, stosuje się konstrukcję {id}(nawiasem mówiąc, jeśli nazwa zmiennej jest taka sama, jak w tym przypadku, to nie musisz podawać jej w nawiasach, ale po prostu ją napisz @PathVariable int id). Dlatego na stronie głównej umieściliśmy linki do każdego filmu, wskazując id:
<a href="/edit/${film.id}">edit</a>
Następnie wartość tę przypisujemy do parametru metody i następnie za jej pomocą pobieramy za pośrednictwem usługi konkretny film z repozytorium i dodajemy go do modelu. To była metoda uzyskania strony do edycji, teraz potrzebujemy metody do samej edycji:
@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;
    }
W metodzie editPagedodaliśmy do modelu atrybut:
modelAndView.addObject("film", filmService.getById(id));
A teraz za pomocą adnotacji @ModelAttributeotrzymujemy ten atrybut i możemy go zmienić. Poproś o metodę POST, ponieważ tutaj przekażemy dane. „ redirect:/” oznacza, że ​​po wykonaniu tej metody zostaniemy przekierowani na adres „ /”, tj. metoda zostanie uruchomiona allFilmsi wrócimy do strony głównej. Teraz zróbmy samą stronę 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>— formularz gromadzenia i przesyłania danych ze wskazaniem, kto będzie je przetwarzał ( /edit)
  • <input>— elementy interfejsu umożliwiające interakcję z użytkownikiem (przyciski, pola wejściowe itp.)
  • <label>- etykieta tekstowa
Zatem po kliknięciu przycisku <input type="submit" value="Edit film">dane z formularza zostaną przesłane na serwer (specjalnie dodano niewidoczne pole z wartością, idaby serwer wiedział, który rekord w bazie wymaga aktualizacji). W metodzie editFilmzostaną one przypisane do odpowiednich pól atrybutów film. Powrócimy wówczas do strony głównej ze zaktualizowaną listą. Strona edycji wygląda następująco: Teraz zacznijmy dodawać nowe filmy do listy. W tym celu potrzebny będzie także formularz do wprowadzania i przesyłania danych. Możesz utworzyć formularz na stronie głównej lub utworzyć osobną stronę, np editPage.jsp. . Ale z drugiej strony forma dodawania będzie dokładnie taka sama jak przy edycji, tj. 4 pola wejściowe i przycisk przesyłania. Po co więc tworzyć nową stronę, użyjmy editPage.jsp. Metoda uzyskania strony:
@RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView addPage() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("editPage");
        return modelAndView;
    }
W metodzie editPagedodatkowo przekazaliśmy atrybut, aby go później zmienić, ale tutaj po prostu otrzymujemy stronę. I sposób dodania:
@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;
    }
Ponieważ nie przekazaliśmy tutaj atrybutu, tutaj zostanie utworzony nowy obiekt Film. Cóż, w zasadzie nie ma tu nic nowego. Warto również zaznaczyć, że obie metody mamy dostępne pod adresem „ /add”. Jest to możliwe dzięki temu, że odpowiadają na różnego rodzaju zapytania. Podążając za linkiem na stronie głównej, wysyłamy żądanie GET, które prowadzi nas do pliku addPage. A kiedy na stronie dodawania klikniemy przycisk wysyłania danych, zostanie wykonane żądanie POST i plik addFilm. Aby dodać nowy film, zdecydowaliśmy się użyć tej samej strony, co do montażu. Ale tam dane są wysyłane na adres „ /edit”:
<c:url value="/edit" var="var"/>
<form action="${var}" method="POST">
    <input type="submit" value="Edit film">
</form>
Musimy nieco ulepszyć stronę, aby zachowywała się inaczej podczas dodawania i edytowania. Aby rozwiązać ten problem, użyjemy warunków z tej samej biblioteki podstawowej 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>
Te. właśnie sprawdzamy pole film.title. Jeśli jest pusty, to jest to nowy film, musimy wypełnić wszystkie dane i dodać go do listy. Jeśli to pole nie jest puste, oznacza to, że jest to film z listy i wystarczy go zmienić. To. otrzymujemy dwie wersje naszej strony: Cóż, ostatnia metoda kontrolera usuwania filmu z listy:
@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;
    }
Myślę, że nie ma tu potrzeby komentowania niczego, to wszystko zostało już rozważone. Linki do tego adresu umieściliśmy już na stronie głównej. Cóż, tutaj wydaje się, że wszystko jest gotowe, możesz uruchomić to jeszcze raz i zobaczyć, jak wszystko działa.

Repozytorium i usługa jako komponenty Spring

Wprowadźmy jeszcze jedną małą poprawkę. Fakt jest taki, że teraz nasz magazyn i serwis to już tylko klasy i żeby z nich skorzystać musimy sami stworzyć obiekt klasy ( new FilmServiceImpl()). Ale nie bez powodu połączyliśmy Springa , więc pozwólmy mu sam kontrolować tę sprawę. Aby oddać nasze klasy pod kontrolę Springa, musimy wskazać, że są one komponentami. W tym celu oznaczamy je specjalnymi adnotacjami:
@Repository
public class FilmDAOImpl implements FilmDAO {
@Service
public class FilmServiceImpl implements FilmService {
Adnotacje @Repositoryi @Service, jak również @Controllerpochodzą z @Component. Jakie są specyficzne cechy i różnice tych trzech adnotacji oraz czym różnią się od prostego komponentu warto przeczytać osobno w dokumentacji lub poradnikach. Na razie wystarczy wiedzieć, że te adnotacje mówią Springowi, że te klasy są odpowiednio repozytorium i usługą. A teraz nie musimy już sami tworzyć konkretnych obiektów tych klas:
private FilmService filmService = new FilmServiceImpl();
Zamiast tego możesz oznaczyć pole specjalną adnotacją, a Spring wybierze odpowiednią implementację:
@Autowired
private FilmService filmService;
Adnotacja @Autowired(automatyczne wiązanie) mówi Springowi, że powinien zagłębić się w swój kontekst i zastąpić tutaj odpowiednią fasolę. Bardzo wygodnie. Jeśli wcześniej korzystaliśmy z interfejsów, aby nie martwić się o konkretną implementację metod, teraz nie musimy się nawet martwić o implementację samego interfejsu, a nawet znać jego nazwę. Pomysł jest taki, że nie zaleca się używania automatycznego wiązania w polu; lepiej jest użyć konstruktora lub ustawiacza. Więcej na ten temat przeczytasz w dokumentacji. Dla nas w zasadzie nie jest to istotne, spokojnie możemy to tak zostawić. Ale ponieważ pomysł tego wymaga, szanujemy, że wszystko jest piękne i bez żółtych ostrzeżeń. W klasie kontrolera utwórzmy seter i dodajmy do niego adnotację:
@Controller
public class FilmController {

    private FilmService filmService;

    @Autowired
    public void setFilmService(FilmService filmService) {
        this.filmService = filmService;
    }
I podobnie robimy setera FilmDAOw klasie FilmServiceImpl. Ciąg dalszy... Przedstawiamy Mavena, Springa, MySQL, Hibernate i pierwszą aplikację CRUD (część 1) Przedstawiamy Mavena, Springa, MySQL, Hibernate i pierwszą aplikację CRUD (część 2) Przedstawiamy Mavena, Springa, MySQL, Hibernate i pierwsza aplikacja CRUD (część 3) Wprowadzenie do Maven, Spring, MySQL, Hibernate i pierwsza aplikacja CRUD (część 4)
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION