午安. 在這篇文章中,我想分享我在創建一個簡單的 CRUD 應用程式的過程中第一次接觸到 Maven、Spring、Hibernate、MySQL 和 Tomcat 等東西。這是 4 的第二部分。本文主要針對那些已經完成 30-40 級,但尚未超越純 Java 並剛開始(或即將開始)進入開放世界的人所有這些技術、框架和其他陌生的詞語。 這是文章《Maven、Spring、MySQL、Hibernate 簡介和第一個 CRUD 應用程式》的第二部分。第一部分可以透過以下連結查看: Maven、Spring、MySQL、Hibernate 和第一個 CRUD 應用程式簡介(第 1 部分)
內容:
好吧,讓我們繼續,現在讓我們嘗試建立一個完整的電影儲存庫。當然,在我們的小而簡單的應用程式中,您可以簡單地愚蠢地將所有邏輯放在控制器中,但是,正如已經指出的,最好立即學習如何正確地完成所有操作。因此,讓我們創建幾個層。我們將有一個負責處理資料的DAO ,一個 Service,其中會有各種其他邏輯,而Controller將只處理請求並呼叫必要的服務方法。資料存取對象
資料存取物件(DAO)就是這樣的一種設計模式。重點是創建一個特殊層,專門負責存取資料(使用資料庫或其他儲存機制)。在套件中dao
我們將建立一個接口FilmDAO
,其中會有新增、刪除等方法。我對它們的稱呼略有不同,但它們對應於基本的CRUD操作(C reate、Read、U pdate、D elete)。
這裡值得注意的是,除了DAO之外,還有Repository這樣的方法,它們看起來非常相似,都是用來處理資料。我還沒有弄清楚這些方法有什麼特點以及它們之間有什麼區別。因此,我在這裡可能會弄錯,這應該稱為存儲庫,而不是道,或者可能是介於兩者之間的東西。但在我見過和研究過的大多數例子中,這被稱為 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 Map
,以便方便地透過其檢索特定電影id
,而無需遍歷整個列表。對於生成,id
我們使用AtomicInteger。讓我們創建一個類FilmDAOImpl
,實現所有方法並填寫地圖。類似的事情。
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 中的方法。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);
}
}
項目結構現在如下所示:
控制器和視圖
現在讓我們研究控制器方法並填充頁面。在填寫頁面時,我們需要一些技巧。例如,要顯示電影列表,我們需要一個循環,例如,如果我們要更改一些銘文,根據參數,我們需要條件等。JSP(JavaServer Pages)格式可讓您使用 Java 程式碼插入來實現這一切。但我不想在頁面上使用java程式碼和HTML程式碼混合。至少,這將是非常醜陋的。幸運的是,為了解決這個問題,有一個很棒的東西,例如JSTL(JavaServer Pages 標準標記庫)或 JSP 標準標記庫。它允許我們在 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 的控制之下,我們需要表明它們是元件。為此,我們用特殊註釋來標記它們:
@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 它應該深入了解其上下文並在此處替換合適的 bean。非常舒服。如果說以前我們使用介面是為了不用擔心方法的具體實現的話,那麼現在我們甚至不需要擔心介面本身的實現,甚至不需要知道它的名字。這個想法是不建議在字段上使用自動綁定;最好使用建構函數或設定器。在文件中閱讀有關此內容的更多資訊。對我們來說,原則上,這並不重要,我們可以放心地就這樣離開。但是,既然這個想法是要求的,我們就會尊重一切都是美麗的,沒有任何黃色警告。在控制器類別中,我們建立一個 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 應用程式(第 3 部分) Maven、Spring、MySQL、Hibernate 簡介和第一個 CRUD 應用程式(第 4 部分)
GO TO FULL VERSION