JavaRush /Blog Java /Random-VI /Giới thiệu Maven, Spring, MySQL, Hibernate và ứng dụng CR...
Макс
Mức độ

Giới thiệu Maven, Spring, MySQL, Hibernate và ứng dụng CRUD đầu tiên (phần 2)

Xuất bản trong nhóm
Chào buổi chiều. Trong bài viết này tôi muốn chia sẻ lần đầu tiên tôi làm quen với những thứ như Maven, Spring, Hibernate, MySQL và Tomcat trong quá trình tạo một ứng dụng CRUD đơn giản. Đây là phần thứ hai của 4. Bài viết chủ yếu dành cho những người đã hoàn thành 30-40 cấp độ ở đây, nhưng chưa vượt ra ngoài Java thuần túy và mới bắt đầu (hoặc sắp bắt đầu) bước vào thế giới mở với tất cả những công nghệ, khuôn khổ này và những từ xa lạ khác. Đây là phần thứ hai của bài viết “Giới thiệu về Maven, Spring, MySQL, Hibernate và ứng dụng CRUD đầu tiên”. Phần đầu tiên các bạn có thể xem theo link sau: Giới thiệu về Maven, Spring, MySQL, Hibernate và ứng dụng CRUD đầu tiên (phần 1)

Nội dung:

Chà, hãy tiếp tục, bây giờ chúng ta hãy thử gợi lên cả một kho phim. Tất nhiên, trong ứng dụng nhỏ và đơn giản của chúng tôi, bạn có thể đơn giản đặt tất cả logic ngay vào bộ điều khiển một cách ngu ngốc, nhưng, như đã lưu ý, tốt hơn là bạn nên học ngay cách thực hiện mọi thứ một cách chính xác. Vì vậy, hãy tạo ra một số lớp. Chúng ta sẽ có một DAO chịu trách nhiệm làm việc với dữ liệu, một Dịch vụ , trong đó sẽ có tất cả các loại logic khác và Bộ điều khiển sẽ chỉ xử lý các yêu cầu và gọi các phương thức dịch vụ cần thiết.

Đối tượng truy cập dữ liệu

Đối tượng truy cập dữ liệu (DAO) là một mẫu thiết kế như vậy. Vấn đề là tạo ra một lớp đặc biệt sẽ tự chịu trách nhiệm truy cập dữ liệu (làm việc với cơ sở dữ liệu hoặc cơ chế lưu trữ khác). Trong gói này, daochúng ta sẽ tạo một giao diện FilmDAOtrong đó sẽ có các phương thức như thêm, xóa, v.v. Tôi gọi chúng hơi khác một chút, nhưng chúng tương ứng với các thao tác CRUD cơ bản ( C reate, Read , U pdate, D elete).

Điều đáng chú ý ở đây là ngoài DAO, còn có một cách tiếp cận như Repository, chúng có vẻ rất giống nhau, cả hai đều được sử dụng để làm việc với dữ liệu. Tôi vẫn chưa tìm ra những đặc điểm mà các phương pháp này có và sự khác biệt giữa chúng là gì. Vì vậy, tôi có thể nhầm ở đây và đây nên được gọi là kho lưu trữ chứ không phải Tao, hoặc có thể nó là thứ gì đó ở giữa. Nhưng trong hầu hết các ví dụ mà tôi đã xem và nghiên cứu thì cái này được gọi là DAO nên chắc tôi cũng sẽ gọi như vậy. Đồng thời, có lẽ ở đâu đó xa hơn trong văn bản tôi sẽ sử dụng kho từ. Trong mọi trường hợp, nếu tôi sai ở đâu đó, xin vui lòng tha thứ cho tôi.

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);
}
Bây giờ chúng ta cần thực hiện nó. Chúng ta vẫn chưa kết nối cơ sở dữ liệu, vẫn còn hơi đáng sợ. Để thực hành và làm quen, trước tiên chúng ta hãy mô phỏng việc lưu trữ trong bộ nhớ và tạo một danh sách gồm một số phim. Để lưu trữ danh sách, chúng ta sẽ sử dụng not List, but Map, để thuận tiện cho việc truy xuất một bộ phim cụ thể theo đó idmà không cần xem qua toàn bộ danh sách. Đối với thế hệ idchúng tôi sử dụng AtomicInteger . Hãy tạo một lớp FilmDAOImpl, triển khai tất cả các phương thức và điền vào bản đồ. Một cái gì đó như thế.
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);
    }
}

Dịch vụ

Bây giờ hãy thêm một lớp dịch vụ. Về nguyên tắc, trong ví dụ này, bạn hoàn toàn có thể thực hiện mà không cần nó, chỉ giới hạn ở DAO; ứng dụng sẽ rất đơn giản và không có kế hoạch nào cho bất kỳ logic phức tạp nào trong dịch vụ. Nhưng đột nhiên, trong tương lai, bạn muốn thêm đủ thứ phức tạp và thú vị vào dự án, vì vậy để hoàn thiện, hãy để nó như vậy. Hiện tại, nó sẽ chỉ gọi các phương thức từ DAO. serviceHãy tạo một giao diện trong gói 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);
}
Và việc thực hiện 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);
    }
}
Cấu trúc dự án bây giờ trông như thế này:
Giới thiệu Maven, Spring, MySQL, Hibernate và ứng dụng CRUD đầu tiên (phần 2) - 1

Bộ điều khiển và Chế độ xem

Bây giờ chúng ta hãy làm việc với các phương thức của bộ điều khiển và điền vào các trang. Khi điền vào các trang, chúng ta sẽ cần một số kỹ thuật. Ví dụ: để hiển thị danh sách phim, chúng ta cần một vòng lặp, nếu chúng ta muốn thay đổi một số dòng chữ, tùy thuộc vào các tham số, chúng ta cần các điều kiện, v.v. Định dạng JSP (Trang JavaServer) cho phép bạn sử dụng các phần chèn mã java để có thể triển khai tất cả những điều này. Nhưng tôi không muốn sử dụng mã java trộn lẫn với mã HTML trên trang. Ở mức tối thiểu, nó sẽ rất xấu. May mắn thay, để giải quyết vấn đề này, có một thứ tuyệt vời như JSTL (Thư viện thẻ tiêu chuẩn các trang JavaServer) hoặc Thư viện thẻ tiêu chuẩn JSP. Nó cho phép chúng tôi sử dụng rất nhiều thẻ bổ sung trong các trang JSP của mình cho nhiều nhu cầu khác nhau. Hãy kết nối nó với pom.xml:
<dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
</dependency>
Bây giờ chúng ta hãy nhìn vào bộ điều khiển. Trước hết, hãy xóa việc tạo đối tượng khỏi đó Film, việc này được thực hiện để thử nghiệm và chúng tôi không cần bất cứ điều gì khác. Hãy thêm một dịch vụ ở đó và gọi các phương thức của nó.
public class FilmController {
    private FilmService filmService = new FilmServiceImpl();
Vâng, theo đó chúng ta sẽ tạo các phương thức cho từng trường hợp, thêm, xóa, v.v. Đầu tiên là phương pháp hiển thị trang chính với danh sách phim:
@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;
    }
Không có gì mới ở đây cả. Chúng tôi nhận được danh sách phim từ dịch vụ và thêm nó vào mô hình. Bây giờ hãy tạo trang chính films.jspmà phương thức này trả về:
<%@ 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>
Chúng ta hãy xem xét kỹ hơn trang này để xem những gì. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - ở đây lõi JSTL được kết nối, bao gồm các thẻ chính để tạo chu trình, điều kiện, v.v. .
  • <table>- thẻ để tạo bảng.
  • <tr>— hàng bàn
  • <th>- tiêu đề cột
  • <td>- ô bảng
Đầu tiên, chúng ta tạo một hàng tiêu đề của bảng với tên của các cột. Sau đó <c:forEach var="film" items="${filmsList}">, trong một vòng lặp (mà chúng tôi lấy từ lõi JSTL), chúng tôi duyệt qua tất cả các phần tử của danh sách đã truyền ( filmsList), với mỗi phần tử ( film), chúng tôi tạo một hàng mới và ghi giá trị tương ứng vào mỗi ô. Có một điểm ở đây, hình như việc ghi âm film.idcần được hiểu là film.getId(), tức là. Trường này không được truy cập trực tiếp nhưng getter được gọi. Ở cột cuối cùng ( action) chúng ta tạo liên kết để xóa và chỉnh sửa (bây giờ chúng ta sẽ tạo các phương thức tương ứng). Vâng, bên dưới là liên kết đến phương pháp thêm phim mới. Nó trông như thế này: Tiếp theo, chúng ta hãy xem phương pháp sẽ trả về trang chỉnh sửa cho một bộ phim cụ thể:
@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;
    }
Một cái gì đó mới đã xuất hiện ở đây - đây là một chú thích @PathVariable. Nó chỉ ra rằng tham số này ( int id) được lấy từ thanh địa chỉ. Để chỉ ra vị trí của tham số này trên thanh địa chỉ, cấu trúc được sử dụng {id}(nhân tiện, nếu tên biến giống nhau, như trong trường hợp này, thì bạn không cần phải chỉ ra nó trong ngoặc đơn mà chỉ cần viết nó @PathVariable int id). Vì vậy, trên trang chính chúng tôi đã tạo liên kết cho từng bộ phim cho biết id:
<a href="/edit/${film.id}">edit</a>
Sau đó, giá trị này được gán cho tham số phương thức và sau đó chúng tôi sử dụng nó để lấy một bộ phim cụ thể từ kho lưu trữ thông qua dịch vụ và thêm nó vào mô hình. Đây là phương pháp để lấy trang chỉnh sửa, bây giờ chúng ta cần một phương pháp để tự chỉnh sử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;
    }
Trong phương thức này, editPagechúng tôi đã thêm một thuộc tính vào mô hình:
modelAndView.addObject("film", filmService.getById(id));
Và bây giờ với sự trợ giúp của chú thích, @ModelAttributechúng ta có được thuộc tính này và có thể thay đổi nó. Phương thức yêu cầu POSTvì ở đây chúng tôi sẽ truyền dữ liệu. " redirect:/" có nghĩa là sau khi thực hiện phương thức này, chúng ta sẽ được chuyển hướng đến địa chỉ " /", tức là. phương thức sẽ chạy allFilmsvà chúng ta sẽ quay lại trang chính. Bây giờ hãy tự tạo trang đó 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>— một biểu mẫu để thu thập và gửi dữ liệu, cho biết ai sẽ xử lý dữ liệu đó ( /edit)
  • <input>- các thành phần giao diện để tương tác với người dùng (nút, trường nhập, v.v.)
  • <label>- nhãn văn bản
Vì vậy, khi bạn nhấp vào nút, <input type="submit" value="Edit film">dữ liệu từ biểu mẫu sẽ được gửi đến máy chủ (một trường vô hình với giá trị đã được thêm đặc biệt idđể máy chủ biết bản ghi nào trong cơ sở dữ liệu cần được cập nhật). Trong phương thức editFilmchúng sẽ được gán cho các trường thuộc tính tương ứng film. Sau đó chúng tôi sẽ quay lại trang chính với danh sách cập nhật. Trang chỉnh sửa trông như thế này: Bây giờ chúng ta hãy bắt đầu thêm phim mới vào danh sách. Để làm điều này, bạn cũng sẽ cần một biểu mẫu để nhập và gửi dữ liệu. Bạn có thể tạo một biểu mẫu trên trang chính hoặc bạn có thể tạo một trang riêng, như editPage.jsp. Tuy nhiên, mặt khác, hình thức thêm sẽ giống hệt như khi chỉnh sửa, tức là. 4 trường đầu vào và một nút gửi. Vậy tại sao phải tạo một trang mới, hãy sử dụng editPage.jsp. Phương pháp để có được trang:
@RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView addPage() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("editPage");
        return modelAndView;
    }
Trong phương thức này, editPagechúng tôi cũng đã chuyển thuộc tính này để thay đổi nó sau này, nhưng ở đây chúng tôi chỉ nhận được trang. Và phương pháp để thêm:
@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;
    }
Vì chúng ta không truyền thuộc tính vào đây nên một đối tượng mới sẽ được tạo ở đây Film. Chà, về cơ bản thì không có gì mới ở đây cả. Cũng cần lưu ý rằng chúng tôi có sẵn cả hai phương pháp tại " /add". Điều này có thể thực hiện được do họ đáp ứng các loại yêu cầu khác nhau. Bằng cách nhấp vào liên kết trên trang chính, chúng tôi thực hiện yêu cầu GET, dẫn chúng tôi đến tệp addPage. Và khi trên trang thêm, chúng tôi nhấp vào nút để gửi dữ liệu, yêu cầu POST được thực hiện và tệp addFilm. Để thêm một bộ phim mới, chúng tôi quyết định sử dụng cùng một trang để chỉnh sửa. Nhưng ở đó dữ liệu được gửi đến địa chỉ " /edit":
<c:url value="/edit" var="var"/>
<form action="${var}" method="POST">
    <input type="submit" value="Edit film">
</form>
Chúng ta cần điều chỉnh trang một chút để nó hoạt động khác khi thêm và chỉnh sửa. Để giải quyết vấn đề này, chúng ta sẽ sử dụng các điều kiện từ cùng thư viện lõi 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>
Những thứ kia. chúng tôi chỉ đang kiểm tra hiện trường film.title. Nếu trống thì đó là phim mới, chúng ta phải điền đầy đủ dữ liệu cho nó và thêm vào danh sách. Nếu trường này không trống thì đó là phim trong danh sách và bạn chỉ cần thay đổi nó. Cái đó. chúng tôi nhận được hai phiên bản của trang của mình: Chà, phương pháp điều khiển cuối cùng để xóa phim khỏi danh sách:
@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;
    }
Tôi nghĩ không cần phải bình luận gì ở đây, tất cả điều này đã được xem xét rồi. Chúng tôi đã tạo liên kết đến địa chỉ này trên trang chính. Chà, mọi thứ dường như đã sẵn sàng ở đây, bạn có thể chạy lại và xem mọi thứ hoạt động như thế nào.

Kho lưu trữ và dịch vụ dưới dạng thành phần mùa xuân

Hãy thực hiện một chỉnh sửa nhỏ nữa. Thực tế là bây giờ bộ lưu trữ và dịch vụ của chúng ta chỉ là các lớp và để sử dụng chúng, chúng ta phải tự tạo một đối tượng lớp ( new FilmServiceImpl()). Nhưng chúng ta kết nối Spring là có lý do , vậy nên hãy để anh ấy tự mình kiểm soát vấn đề này. Để đặt các lớp của chúng ta dưới sự kiểm soát của Spring, chúng ta cần chỉ ra rằng chúng là các thành phần. Để làm điều này, chúng tôi đánh dấu chúng bằng các chú thích đặc biệt:
@Repository
public class FilmDAOImpl implements FilmDAO {
@Service
public class FilmServiceImpl implements FilmService {
Các chú thích @Repository@Service, cũng như @Controllercó nguồn gốc từ @Component. Các tính năng và sự khác biệt cụ thể của ba chú thích này là gì và chúng khác với một thành phần đơn giản như thế nào nên được đọc riêng trong tài liệu hoặc hướng dẫn. Hiện tại, chỉ cần biết rằng các chú thích này cho Spring biết rằng các lớp này lần lượt là một kho lưu trữ và một dịch vụ. Và bây giờ chúng ta không cần phải tự tạo các đối tượng cụ thể của các lớp này nữa:
private FilmService filmService = new FilmServiceImpl();
Thay vào đó, bạn có thể đánh dấu trường bằng chú thích đặc biệt và Spring sẽ chọn cách triển khai phù hợp:
@Autowired
private FilmService filmService;
Chú thích @Autowired(tự động liên kết) cho Spring biết rằng nó nên đi sâu vào ngữ cảnh của nó và thay thế một loại đậu phù hợp ở đây. Rất thoải mái. Nếu trước đây chúng ta sử dụng giao diện để không phải lo lắng về việc triển khai các phương thức cụ thể thì giờ đây chúng ta thậm chí không cần phải lo lắng về việc triển khai giao diện đó và thậm chí còn biết tên của nó. Ý tưởng là không nên sử dụng tính năng tự động liên kết trên một trường; tốt hơn nên sử dụng hàm tạo hoặc trình thiết lập. Đọc thêm về điều này trong tài liệu. Đối với chúng tôi, về nguyên tắc, điều này không quan trọng, chúng tôi có thể yên tâm để nó như vậy. Tuy nhiên, vì ý tưởng đang yêu cầu nên chúng tôi sẽ tôn trọng rằng mọi thứ đều đẹp đẽ và không có bất kỳ cảnh báo màu vàng nào. Trong lớp trình điều khiển, hãy tạo một setter và chú thích nó:
@Controller
public class FilmController {

    private FilmService filmService;

    @Autowired
    public void setFilmService(FilmService filmService) {
        this.filmService = filmService;
    }
Và tương tự như vậy, chúng ta tạo một setter cho FilmDAOtrong lớp FilmServiceImpl. Còn tiếp... Giới thiệu Maven, Spring, MySQL, Hibernate và ứng dụng CRUD đầu tiên (phần 1) Giới thiệu Maven, Spring, MySQL, Hibernate và ứng dụng CRUD đầu tiên (phần 2) Giới thiệu Maven, Spring, MySQL, Hibernate và Ứng dụng CRUD đầu tiên (phần 3) Giới thiệu về Maven, Spring, MySQL, Hibernate và ứng dụng CRUD đầu tiên (phần 4)
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION