JavaRush /Java Blog /Random-KO /Maven, Spring, MySQL, Hibernate 및 첫 번째 CRUD 애플리케이션 소개(2부)...
Макс
레벨 41

Maven, Spring, MySQL, Hibernate 및 첫 번째 CRUD 애플리케이션 소개(2부)

Random-KO 그룹에 게시되었습니다
좋은 오후에요. 이 기사에서는 간단한 CRUD 애플리케이션을 생성하는 과정에서 Maven, Spring, Hibernate, MySQL 및 Tomcat과 같은 것들을 처음 접한 내용을 공유하고 싶습니다. 이것은 4의 두 번째 부분입니다. 이 기사는 주로 여기에서 이미 30-40 레벨을 완료했지만 아직 순수 Java를 넘어서는 모험을 하지 않고 오픈 월드에 들어가기 시작하는(또는 곧 시작하려는) 사람들을 위한 것입니다. 이 모든 기술, 프레임워크 및 기타 생소한 단어입니다. 이것은 "Maven, Spring, MySQL, Hibernate 및 첫 번째 CRUD 애플리케이션 소개" 기사의 두 번째 부분입니다. 첫 번째 부분은 다음 링크를 통해 볼 수 있습니다: Maven, Spring, MySQL, Hibernate 및 첫 번째 CRUD 애플리케이션 소개(1부)

콘텐츠:

글쎄, 계속해서 전체 영화 저장소를 만들어 봅시다. 물론 작고 간단한 응용 프로그램에서는 모든 논리를 컨트롤러에 어리석게 넣을 수 있지만 이미 언급했듯이 모든 것을 올바르게 수행하는 방법을 즉시 배우는 것이 좋습니다. 그러므로 여러 개의 레이어를 만들어 보겠습니다. 우리는 데이터 작업을 담당하는 DAO , 즉 모든 종류의 다른 로직이 있는 서비스를 갖게 될 것이며 컨트롤러는 요청만 처리하고 필요한 서비스 메소드를 호출할 것입니다.

데이터 액세스 객체

DAO(Data Access Object)는 그러한 디자인 패턴입니다. 요점은 데이터 액세스(데이터베이스 또는 기타 저장 메커니즘 사용)만 담당하는 특수 레이어를 만드는 것입니다. 패키지에는 추가, 삭제 등과 같은 메소드가 있는 dao인터페이스를 생성합니다. FilmDAO조금 다르게 부르긴 했지만 기본적인 CRUD 작업( C reate, Read , Update , D elete)에 해당합니다.

여기서는 DAO 외에도 리포지토리와 같은 접근 방식도 있는데 매우 유사해 보이며 둘 다 데이터 작업에 사용된다는 점에 주목할 가치가 있습니다. 나는 이러한 접근 방식이 어떤 기능을 가지고 있는지, 그리고 이들 접근 방식의 차이점이 무엇인지 아직 파악하지 못했습니다. 그러므로 여기서는 제가 틀릴 수도 있습니다. 이것을 Tao가 아닌 저장소라고 불러야 합니다. 아니면 그 사이에 있는 것일 수도 있습니다. 하지만 제가 보고 연구한 대부분의 예에서는 이것을 DAO라고 부르므로 아마도 동일하게 부를 것입니다. 동시에, 아마도 본문의 어딘가에서 저장소라는 단어를 사용할 것입니다. 어쨌든 제가 틀린 부분이 있다면 용서해주세요.

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);
}
이제 구현이 필요합니다. 아직 데이터베이스를 연결하지 않겠습니다. 아직은 조금 무섭습니다. 연습하고 익숙해지기 위해 먼저 메모리 저장을 시뮬레이션하고 여러 영화로 목록을 만들어 보겠습니다. 목록을 저장하기 위해 전체 목록을 거치지 않고 특정 영화를 편리하게 검색할 수 있도록 not List, but 을 사용합니다 . 생성에는 AtomicInteger를 사용합니다 . 클래스를 만들고 , 모든 메소드를 구현하고, 지도를 채워봅시다. 그런 것. MapididFilmDAOImpl
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);
    }
}

서비스

이제 서비스 계층을 추가해 보겠습니다. 원칙적으로 이 예에서는 DAO로 제한하여 DAO 없이도 수행할 수 있습니다. 애플리케이션은 매우 간단하며 서비스에 복잡한 로직에 대한 계획은 없습니다. 그러나 갑자기 미래에는 프로젝트에 모든 종류의 복잡함과 흥미로운 것들을 추가하고 싶을 것이므로 완전성을 위해 그대로 두십시오. 지금은 DAO에서 메서드를 호출하기만 하면 됩니다. 패키지에 service인터페이스를 만들어 보겠습니다 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);
}
구현은 다음과 같습니다.
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);
    }
}
이제 프로젝트 구조는 다음과 같습니다.
Maven, Spring, MySQL, Hibernate 및 첫 번째 CRUD 애플리케이션 소개(2부) - 1

컨트롤러와 뷰

이제 컨트롤러 메서드에 대해 작업하고 페이지를 채우겠습니다. 페이지를 작성할 때 몇 가지 기술이 필요합니다. 예를 들어, 영화 목록을 표시하려면 주기가 필요합니다. 예를 들어 매개 변수에 따라 일부 비문을 변경하려면 조건이 필요합니다. JSP(JavaServer Pages) 형식을 사용하면 이 모든 것을 구현할 수 있는 Java 코드 삽입을 사용할 수 있습니다. 하지만 페이지에서 HTML 코드와 Java 코드를 혼합하여 사용하고 싶지 않습니다. 최소한 매우 추악한 일이 될 것입니다. 다행히도 이 문제를 해결하기 위해 JSTL (JavaServer Pages Standard Tag Library)이나 JSP Standard Tag Library와 같은 훌륭한 방법이 있습니다 . 이를 통해 JSP 페이지에서 다양한 요구에 맞게 추가 태그 전체를 사용할 수 있습니다. 다음과 같이 연결해 보겠습니다 pom.xml.
<dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
</dependency>
이제 컨트롤러를 살펴보겠습니다. 우선 거기에서 객체 생성을 제거해 보겠습니다 Film. 이는 테스트용으로 수행되었으며 다른 것은 필요하지 않습니다. 거기에 서비스를 추가하고 해당 메서드를 호출해 보겠습니다.
public class FilmController {
    private FilmService filmService = new FilmServiceImpl();
그럼 그에 따라 각 사례에 대한 메서드를 만들고 추가, 삭제 등을 하겠습니다. 먼저 영화 목록이 포함된 메인 페이지를 표시하는 방법은 다음과 같습니다.
@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;
    }
여기에는 새로운 것이 없습니다. 서비스에서 영화 목록을 가져와 모델에 추가합니다. films.jsp이제 이 메서드가 반환하는 기본 페이지를 만들어 보겠습니다 .
<%@ 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>
이 페이지를 자세히 살펴보고 무엇이 무엇인지 살펴보겠습니다. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - 여기에는 주기, 조건 등을 생성하기 위한 기본 태그가 포함된 JSTL 코어가 연결되어 있습니다. .
  • <table>— 테이블 생성을 위한 태그입니다.
  • <tr>— 테이블 행
  • <th>- 열 헤더
  • <td>— 테이블 셀
먼저 열 이름으로 테이블 헤더 행을 만듭니다. 그런 다음 <c:forEach var="film" items="${filmsList}">JSTL 코어에서 가져온 루프에서 전달된 목록( filmsList)의 모든 요소를 ​​살펴보고 각 요소( film)에 대해 새 행을 만들고 해당 값을 각 셀에 씁니다. 여기에 한 가지 점이 있는데, 녹음은 다음 film.id과 같이 이해되어야 할 것 같습니다 film.getId(). 필드에 직접 액세스할 수는 없지만 getter가 호출됩니다. 마지막 열( action)에서는 삭제 및 편집을 위한 링크를 만듭니다(이제 해당 메소드를 생성하겠습니다). 음, 아래는 새 영화를 추가하는 방법에 대한 링크입니다. 그 모습은 다음과 같습니다. 다음으로 특정 영화에 대한 편집 페이지를 반환하는 메서드를 살펴보겠습니다.
@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;
    }
여기에 새로운 것이 나타났습니다. 이것은 주석입니다 @PathVariable. 이는 이 매개변수( int id)가 주소 표시줄에서 가져온 것임을 나타냅니다. 주소 표시줄에 이 매개 변수의 위치를 ​​표시하기 위해 구성이 사용됩니다 {id}(그런데 변수 이름이 이 경우와 동일하면 괄호 안에 표시할 필요 없이 그냥 쓰면 됩니다). @PathVariable int id). 따라서 메인 페이지에는 다음을 나타내는 각 영화에 대한 링크가 만들어졌습니다 id.
<a href="/edit/${film.id}">edit</a>
그런 다음 이 값이 메소드 매개변수에 할당된 다음 이를 사용하여 서비스를 통해 저장소에서 특정 영화를 가져와 모델에 추가합니다. 이것이 편집 페이지를 가져오는 방법이었습니다. 이제 자체 편집을 위한 방법이 필요합니다.
@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;
    }
이 방법에서는 editPage모델에 속성을 추가했습니다.
modelAndView.addObject("film", filmService.getById(id));
이제 주석의 도움으로 @ModelAttribute이 속성을 얻고 변경할 수 있습니다. 요청 방법은 POST여기에서 데이터를 전달하기 때문입니다. " redirect:/"는 이 메소드를 실행한 후 " " 주소로 리디렉션된다는 의미입니다 /. 즉, 메서드가 실행되고 allFilms기본 페이지로 돌아갑니다. 이제 페이지 자체를 만들어 보겠습니다 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>— 데이터를 수집하고 전송하기 위한 양식으로, 누가 데이터를 처리할지 표시합니다( /edit).
  • <input>— 사용자 상호작용을 위한 인터페이스 요소(버튼, 입력 필드 등)
  • <label>- 텍스트 라벨
따라서 버튼을 클릭하면 <input type="submit" value="Edit film">양식의 데이터가 서버로 전송됩니다( id데이터베이스의 어떤 레코드를 업데이트해야 하는지 서버가 알 수 있도록 값이 포함된 보이지 않는 필드가 특별히 추가되었습니다). 이 방법에서는 editFilm해당 속성 필드에 할당됩니다 film. 그런 다음 업데이트된 목록이 있는 기본 페이지로 돌아갑니다. 편집 페이지는 다음과 같습니다. 이제 목록에 새 영화를 추가해 보겠습니다. 이를 위해서는 데이터를 입력하고 제출하기 위한 양식도 필요합니다. 메인 페이지에 양식을 만들 수도 있고, 처럼 별도의 페이지를 만들 수도 있습니다 editPage.jsp. 그러나 반면에 추가 형식은 편집 형식과 완전히 동일합니다. 4개의 입력 필드와 제출 버튼. 그렇다면 새 페이지를 만드는 이유는 무엇입니까 editPage.jsp? 페이지를 얻는 방법:
@RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView addPage() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("editPage");
        return modelAndView;
    }
메소드에서는 editPage나중에 변경하기 위해 속성을 추가로 전달했지만 여기서는 페이지만 수신합니다. 그리고 추가하는 방법은 다음과 같습니다.
@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;
    }
여기에 속성을 전달하지 않았으므로 여기에 새 객체가 생성됩니다 Film. 글쎄요, 기본적으로 여기에는 새로운 것이 없습니다. " "에서 두 가지 방법을 모두 사용할 수 있다는 점도 주목할 가치가 있습니다 /add. 이는 다양한 유형의 요청에 응답하기 때문에 가능합니다. 메인 페이지의 링크를 따라가면 GET 요청이 생성되어 addPage. 추가 페이지에서 버튼을 클릭하여 데이터를 전송하면 POST 요청이 이루어지고 addFilm. 새 영화를 추가하기 위해 편집과 동일한 페이지를 사용하기로 결정했습니다. 하지만 데이터는 " "주소로 전송됩니다 /edit.
<c:url value="/edit" var="var"/>
<form action="${var}" method="POST">
    <input type="submit" value="Edit film">
</form>
추가 및 편집 시 다르게 동작하도록 페이지를 약간 조정해야 합니다. 이 문제를 해결하기 위해 동일한 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>
저것들. 우리는 단지 필드를 확인하고 있습니다 film.title. 비어 있으면 새 영화이므로 모든 데이터를 입력하고 목록에 추가해야 합니다. 이 필드가 비어 있지 않으면 목록에 있는 영화이므로 변경하기만 하면 됩니다. 저것. 페이지에는 두 가지 버전이 있습니다. 목록에서 영화를 제거하는 마지막 컨트롤러 메서드는 다음과 같습니다.
@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;
    }
나는 여기에 대해 아무것도 언급할 필요가 없다고 생각합니다. 이 모든 것은 이미 고려되었습니다. 우리는 이미 메인 페이지에 이 주소에 대한 링크를 만들었습니다. 여기에서는 모든 것이 준비된 것 같습니다. 다시 실행하여 모든 것이 어떻게 작동하는지 확인할 수 있습니다.

Spring 컴포넌트로서의 리포지토리 및 서비스

한 가지 더 작은 수정을 해보자. 사실 우리의 스토리지와 서비스는 단지 클래스일 뿐이며, 이를 사용하려면 클래스 객체를 직접 생성해야 합니다( new FilmServiceImpl()). 그러나 우리는 Spring을 연결한 이유가 있으므로 Spring이 이 문제를 스스로 제어하도록 하십시오. 클래스를 Spring의 제어하에 두려면 해당 클래스가 컴포넌트임을 표시해야 합니다. 이를 위해 특별한 주석을 표시합니다.
@Repository
public class FilmDAOImpl implements FilmDAO {
@Service
public class FilmServiceImpl implements FilmService {
주석 @Repository@Service은(는) @Controller에서 파생됩니다 @Component. 이 세 가지 주석의 구체적인 기능과 차이점은 무엇이며 단순한 구성 요소와 어떻게 다른지는 문서나 가이드에서 별도로 읽어야 합니다. 지금은 이러한 주석이 Spring에 해당 클래스가 각각 저장소와 서비스임을 알려주는 것만 알면 충분합니다. 이제 더 이상 이러한 클래스의 구체적인 개체를 직접 만들 필요가 없습니다.
private FilmService filmService = new FilmServiceImpl();
대신에 특수 주석으로 필드를 표시할 수 있으며 Spring은 적절한 구현을 선택합니다.
@Autowired
private FilmService filmService;
주석 @Autowired(자동 바인딩)은 Spring에게 컨텍스트를 조사하고 여기에서 적절한 빈을 대체해야 한다고 알려줍니다. 매우 편안합니다. 이전에 인터페이스를 사용하여 메소드의 특정 구현에 대해 걱정하지 않았다면 이제는 인터페이스 자체의 구현에 대해 걱정할 필요가 없으며 인터페이스 이름도 알 수 없습니다. 필드에 자동 바인딩을 사용하는 것은 권장되지 않으며 생성자나 설정자를 사용하는 것이 더 좋습니다. 이에 대한 자세한 내용은 설명서를 참조하세요. 원칙적으로 이것은 중요하지 않으므로 안전하게 그대로 둘 수 있습니다. 그러나 아이디어가 요구하는 것이므로 모든 것이 아름답고 노란색 경고가 없다는 점을 존중할 것입니다. 컨트롤러 클래스에서 setter를 생성하고 주석을 달아보겠습니다.
@Controller
public class FilmController {

    private FilmService filmService;

    @Autowired
    public void setFilmService(FilmService filmService) {
        this.filmService = filmService;
    }
FilmDAO그리고 마찬가지로 클래스에 대한 setter를 만듭니다 FilmServiceImpl. 계속하려면... Maven, Spring, MySQL, Hibernate 및 첫 번째 CRUD 애플리케이션 소개(1부) Maven, Spring, MySQL, Hibernate 및 첫 번째 CRUD 애플리케이션 소개(2부) Maven, Spring, MySQL, Hibernate 및 첫 번째 CRUD 애플리케이션 소개 첫 번째 CRUD 애플리케이션(3부) Maven, Spring, MySQL, Hibernate 및 첫 번째 CRUD 애플리케이션 소개(4부)
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION