在前面課程中,咱們學習了 Spring Boot Web 開發、JPA 數據庫操做、Thymeleaf 和頁面交互技術,這節課綜合這些內容作一個用戶管理功能,包括展現用戶列表(分頁)、添加用戶、修改用戶和刪除用戶。有人說程序員的一輩子都是在增、刪、改、查,這句話不必定全對,但也有必定的道理,相比於這句話,我更認同的是這句:程序員的技術學習都是從增、刪、改、查開始的。css
這節課將介紹如何使用 JPA 和 Thymeleaf 作一個用戶管理功能。html
pom 包裏面添加 JPA 和 Thymeleaf 的相關包引用。前端
<dependency>
<groupId></groupId> org.springframework.boot
<artifactId></artifactId> spring-boot-starter-web
</dependency>
<dependency>
<groupId></groupId> org.springframework.boot
<artifactId></artifactId> spring-boot-starter-Thymeleaf
</dependency>
<dependency>
<groupId></groupId> org.springframework.boot
<artifactId></artifactId> spring-boot-starter-data-Jpa
</dependency>
<dependency>
<groupId></groupId> mysql
<artifactId></artifactId> mysql-connector-java
</dependency>
在 application.properties 中添加配置:java
testtruetruespring.datasource.url=jdbc:mysql://localhost:3306/?serverTimezone=UTC&useUnicode=&characterEncoding=utf-8&useSSL=
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
truespring.jpa.show-sql=
falsespring.thymeleaf.cache=
其中,spring.Thymeleaf.cache=false 是關閉 Thymeleaf 的緩存,否則在開發過程當中修改頁面不會馬上生效須要重啓,生產可配置爲 true。mysql
在項目 resources 目錄下會有兩個文件夾:static 目錄用於放置網站的靜態內容如 css、js、圖片;templates 目錄用於放置項目使用的頁面模板。git
啓動類須要添加 Servlet 的支持:程序員
@SpringBootApplication
publicclass JpaThymeleafApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(JpaThymeleafApplication.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(JpaThymeleafApplication.class, args);
}
}
添加 SpringBootServletInitializer 是爲了支持將項目打包成獨立的 war 在 Tomcat 中運行的狀況。github
實體類映射數據庫表:web
@Entity
publicclass User {
@Id
@GeneratedValue
privatelong id;
@Columnfalsetrue (nullable =, unique =)
private String userName;
@Columnfalse (nullable =)
private String passWord;
@Columnfalse (nullable =)
privateint age;
@Columnfalse (nullable =)
private Date regTime;
//省略getter settet方法
}
繼承 JpaRepository 類會自動實現不少內置的方法,包括增、刪、改、查,也能夠根據方法名來自動生成相關 SQL。spring
publicinterface UserRepository extends JpaRepository<User, Long> {
@Query"select u from User u" ()
Page<User> findList(Pageable pageable) ;
User findById(long id) ;
User findByUserName(String userName) ;
void deleteById(Long id) ;
}
Repository 內編寫咱們須要的 SQL 和分頁查詢。
在處理前端業務的時候通常是使用 param 結尾的參數來處理,在項目下新建 param 包,在 param 包下建立 UserParam 類接收添加用戶的請求參數。另外,須要對接收的參數作校驗,按照前面課程的內容,引入 hibernate-validator 作校驗。
publicclass UserParam {
privatelong id;
@NotEmpty"姓名不能爲空" (message=)
private String userName;
@NotEmpty"密碼不能爲空" (message=)
@Length6"密碼長度不能小於6位" (min=,message=)
private String passWord;
@Max100"年齡不能大於100歲" (value =, message =)
@Min18"必須年滿18歲!" (value=,message=)
privateint age;
//省略getter settet方法
}
Controller 負責接收請求,首先判斷參數是否正確,若是有錯誤直接返回頁面,將錯誤信息展現給用戶,再判斷用戶是否存在,若是用戶已經存在一樣返回頁面給出提示。驗證經過後,將 UserParam 屬性複製到 User 並添加用戶註冊時間,最後將用戶信息保存到數據庫中。
"/add"@RequestMapping()
publicString add(@Valid UserParam userParam,BindingResult result, Model model) {
"" String errorMsg=;
// 參數校驗
if (result.hasErrors()) {
Listlist <ObjectError>= result.getAllErrors();
forlist (ObjectError error :) {
"-"";" errorMsg=errorMsg + error.getCode() ++ error.getDefaultMessage() +;
}
"errorMsg" model.addAttribute(,errorMsg);
return"user/userAdd" ;
}
//判斷是否重複添加
User u= userRepository.findByUserName(userParam.getUserName());
ifnull (u!=){
"errorMsg""用戶已存在!" model.addAttribute(,);
return"user/userAdd" ;
}
new User user=User();
BeanUtils.copyProperties(userParam,user);
new user.setRegTime(Date());
//保存
userRepository.save(user);
return"redirect:/list" ;
}
添加用戶部分頁面(userAdd.html)
前端頁面引入了 Bootstrap 前端框架,如下表單按照 Bootstrap 的格式進行設計。
<form class="form-horizontal" th:action="@{/add}" method="post">
<!-- 表單內容-->
<div class="form-group">
<label for="userName" class="col-sm-2 control-label"></label> userName
<div class="col-sm-10">
<input type="text" class="form-control" name="userName" id="userName" placeholder="userName"/>
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-2 control-label" ></label> passWord
<div class="col-sm-10">
<input type="password" class="form-control" name="passWord" id="passWord" placeholder="passWord"/>
</div>
</div>
....
<!-- 錯誤信息展現區-->
<div class="form-group">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-10">
<div th:if="${errorMsg != null}" class="alert alert-danger" role="alert" th:text="${errorMsg}">
</div>
</div>
</div>
<!-- 按鈕區-->
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input type="submit" value="Submit" class="btn btn-info" />
<input type="reset" value="Reset" class="btn btn-info" />
<a href="/toAdd" th:href="@{/list}" class="btn btn-info"></a> Back
</div>
</div>
</form>
效果圖:
參考前面課程,JPA 依賴 Pageable 爲用戶列表頁作分頁,默認每頁展現 6 個用戶,而且按照用戶註冊的倒序來排列,具體信息以下:
"/list"@RequestMapping()
"page""0"public String list(Model model,@RequestParam(value =, defaultValue =) Integer page,
"size""6" @RequestParam(value =, defaultValue =) Integer size) {
new"id" Sort sort =Sort(Sort.Direction.DESC,);
of Pageable pageable = PageRequest.(page, size, sort);
Page<User> users=userRepository.findList(pageable);
"users" model.addAttribute(, users);
return"user/list" ;
}
前端頁抽取一個公共的分頁信息——page.html,頁面部分信息以下:
<div th:if="${(users.totalPages le 10) and (users.totalPages gt 0)}" th:remove="tag">
<div th:each="pg : ${#numbers.sequence(0, users.totalPages - 1)}" th:remove="tag">
<span th:if="${pg eq users.getNumber()}" th:remove="tag">
<li class="active"><span class="current_page line_height" th:text="${pg+1}"></span></li> ${pageNumber}
</span>
<span th:unless="${pg eq users.getNumber()}" th:remove="tag">
<li><a href="#" th:href="@{${pageUrl}(page=${pg})}" th:text="${pg+1}"></a></li>
</span>
</div>
</div>
<li th:if="${users.hasNext()}"><a href="#" th:href="@{${pageUrl}(page=${users.number+1})}"></a></li>下一頁
<li><a href="#" th:href="${users.totalPages le 0 ? pageUrl+'page=0':pageUrl+'&page='+(users.totalPages-1)}"></a></li>尾頁
<li><span th:utext="'共'+${users.totalPages}+'頁 / '+${users.totalElements}+' 條'"></span></li>
page.html 頁面的做用是顯示主頁的頁碼,包括首頁、末頁、第幾頁,共幾頁這類信息,須要根據頁碼的數據進行動態調整。頁面中使用了 Thymeleaf 大量語法:th:if 判斷、th:each 循環、th:href 連接等,分頁信息主要從後端傳遞的 Page 對象獲取。
而後在 list.html 頁面中引入 page.html 頁面分頁信息。
<h1></h1>用戶列表
<br/><br/>
<div class="with:80%">
<table class="table table-hover">
<thead>
<!-- 表頭信息-->
<tr>
<th></th> #
<th></th> User Name
<th></th> Password
<th></th> Age
<th></th> Reg Time
<th></th> Edit
<th></th> Delete
</tr>
</thead>
<tbody>
<!-- 表循環展現用戶信息-->
<tr th:each="user : ${users}">
<th scope="row" th:text="${user.id}"></th> 1
<td th:text="${user.userName}"></td> neo
<td th:text="${user.passWord}"></td> Otto
<td th:text="${user.age}"></td> 6
<td th:text="${#dates.format(user.regTime, 'yyyy/MMM/dd HH:mm:ss')}"></td>
<td><a th:href="@{/toEdit(id=${user.id})}"></a></td> edit
<td><a th:href="@{/delete(id=${user.id})}" onclick="return confirm('確認是否刪除此用戶?')" ></a></td> delete
</tr>
</tbody>
</table>
<!-- 引入分頁內容-->
<div th:include="page :: pager" th:remove="tag"></div>
</div>
<div class="form-group">
<div class="col-sm-2 control-label">
<a href="/toAdd" th:href="@{/toAdd}" class="btn btn-info"></a> add
</div>
</div>
<tr th:each="user : ${users}">
這裏會從 Controler 層 model set 的對象去獲取相關的內容,th:each 表示會循環遍歷對象內容。
效果圖以下:
點擊修改功能的時候,須要帶上用戶的 ID 信息:
<a th:href="@{/toEdit(id=${user.id})}">edit</a></td><td>
後端根據用戶 ID 獲取用戶信息,並放入 Model 中。
@RequestMapping"/toEdit"()
public String toEdit(Model model,Long id) {
User user=userRepository.findById(id);
"user" model.addAttribute(, user);
return"user/userEdit" ;
}
修改頁面展現用戶信息,如下爲 userEdit.html 頁面部份內容:
<form class="form-horizontal" th:action="@{/edit}" th:object="${user}" method="post">
<!--隱藏用戶 ID-->
<input type="hidden" name="id" th:value="*{id}" />
<div class="form-group">
<label for="userName" class="col-sm-2 control-label"></label> userName
<div class="col-sm-10">
<input type="text" class="form-control" name="userName" id="userName" th:value="*{userName}" placeholder="userName"/>
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-2 control-label" ></label> passWord
<div class="col-sm-10">
<input type="password" class="form-control" name="passWord" id="passWord" th:value="*{passWord}" placeholder="passWord"/>
</div>
</div>
<!--錯誤信息-->
<div class="form-group">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-10">
<div th:if="${errorMsg != null}" class="alert alert-danger" role="alert" th:text="${errorMsg}">
</div>
</div>
</div>
<!--按鈕區-->
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input type="submit" value="Submit" class="btn btn-info" />
<a th:href="@{/list}" class="btn btn-info"></a> Back
</div>
</div>
</form>
修改完成後提交到後臺:
"/edit"@RequestMapping()
public String edit(@Valid UserParam userParam, BindingResult result,Model model) {
"" String errorMsg=;
//參數校驗
if (result.hasErrors()) {
list List<ObjectError>= result.getAllErrors();
forlist (ObjectError error :) {
"-"";" errorMsg=errorMsg + error.getCode() ++ error.getDefaultMessage() +;
}
"errorMsg" model.addAttribute(,errorMsg);
"user" model.addAttribute(, userParam);
return"user/userEdit" ;
}
//複製屬性保持修改後數據
new User user=User();
BeanUtils.copyProperties(userParam,user);
new user.setRegTime(Date());
userRepository.save(user);
return"redirect:/list" ;
}
後臺一樣須要進行參數驗證,無誤後修改對應的用戶信息。
效果圖:
單擊刪除按鈕的時候須要用戶再次確認,確認後才能刪除。
<a th:href="@{/delete(id=${user.id})}" onclick="return confirm('確認是否刪除此用戶?')" >delete</a></td><td>
效果以下:
後端根據用戶 ID 進行刪除便可。
"/delete"@RequestMapping()
deletepublic String(Long id) {
delete userRepository.(id);
return"redirect:/list" ;
}
刪除完成以後,再跳轉到用戶列表頁。
用戶管理功能包含了用戶的增長、修改、刪除、展現等功能,也是咱們平常開發中最經常使用的四個功能。在實現用戶管理功能的過程當中使用了 JPA 的增長、修改、刪除、查詢、分頁查詢功能;使用了 Thymeleaf 展現用戶信息,在 list 頁面引入分頁模板,使用了 Thymeleaf 內嵌的 dates 對日期進行了格式化;通過今天的學習較全面演練了前期的學習內容。