天天看點

深入實踐Spring

深入實踐Spring

深入實踐spring boot

陳韶健 著

圖書在版編目(cip)資料

深入實踐spring boot / 陳韶健著. —北京:機械工業出版社,2016.10

isbn 978-7-111-55088-4

i. 深… ii. 陳… iii. java語言-程式設計 iv. tp312

中國版本圖書館cip資料核字(2016)第244089号

出版發行:機械工業出版社(北京市西城區百萬莊大街22号 郵政編碼:100037)

責任編輯:李 藝 責任校對:殷 虹

印  刷: 版  次:2016年11月第1版第1次印刷

開  本:186mm×240mm 1/16 印  張:17.25

書  号:isbn 978-7-111-55088-4 定  價:59.00元

凡購本書,如有缺頁、倒頁、脫頁,由本社發行部調換

客服熱線:(010)88379426 88361066 投稿熱線:(010)88379604

購書熱線:(010)68326294 88379649 68995259 讀者信箱:[email protected]

版權所有 ? 侵權必究

封底無防僞标均為盜版

本書法律顧問:北京大成律師事務所 韓光/鄒曉東

preface?前  言

spring boot作為java程式設計語言的一個全新開發架構,在國内外才剛剛興起,還未得到普及使用。相比于以往的一些開發架構,spring boot不但使用更加簡單,而且功能更加豐富,性能更加穩定而健壯。使用spring boot開發架構,不僅能提高開發速度,增強生産效率,從某種意義上,可以說是解放了程式員的勞動,而且一種新技術的使用,更能增強系統的穩定性和擴充系統的性能名額。本書就是本着提高開發效率,增強系統性能,促進新技術的普及使用這一目的而寫的。

spring boot是在spring架構基礎上建立的一個全新架構,其設計目的是簡化spring應用的搭建和開發過程,它不但具有spring的所有優秀特性,而且具有如下顯著的特點:

為spring開發提供更加簡單的使用和快速開發的技巧。

具有開箱即用的預設配置功能,能根據項目依賴自動配置。

具有功能更加強大的服務體系,包括嵌入式服務、安全、性能名額、健康檢查等。

絕對沒有代碼生成,可以不再需要xml配置,即可讓應用更加輕巧和靈活。

spring boot對于一些第三方技術的使用,提供了非常完美的整合,使你在簡單的使用中,不知不覺運用了非常進階和先進的技術。

雖然spring boot具有這麼多優秀的特性,但它使用起來并不複雜,而且非常簡單,是以不管是java程式開發初學者,還是經驗豐富的開發人員,使用spring boot都是一個理想的選擇。

spring boot發展迅速,自從2014年4月釋出了1.0.0版本,至今已經釋出了1.4.0正式版。現在,spring boot正在不同的角落中悄然興起,估計用不了多久,它将成為java開發的又一個熱潮,為衆多java開發者追捧。

本書将以一些非常切合生産實際的應用執行個體,帶你一起使用spring boot架構,開始一段愉快的快速開發和探索之旅。

關于本書

本書以豐富的執行個體,介紹了如何使用spring boot開發架構進行基礎應用和分布式應用等方面的開發,并且介紹了如何使用spring boot開發的應用搭建一個高性能的服務平台,同時還對spring boot的一些核心功能的源代碼進行了分析,進而加深對spring boot的了解。書中對從最基本的入門知識,到資料庫的使用,以及界面設計、安全設計等領域都做了詳細的介紹和探讨,并在分布式應用系統領域,以平台級應用系統的執行個體,介紹了如何建立和使用sso管理系統、分布式檔案系統,如何使用spring cloud進行雲應用方面的開發,以及如何使用docker釋出和建構高可用的分布式系統服務平台。同時,對spring boot的程式加載、自動配置、資料管理,和spring cloud的配置管理、發現服務和負載均衡服務等核心功能的源代碼做了深入剖析,這樣在認識其實作原理的基礎上,能更好地使用其相應的功能。

全書分為三個部分:第一部分(第1~5章)介紹基礎應用方面的開發,包含簡單入門知識、資料庫使用、界面設計和安全設計等内容;第二部分(第6~9章)介紹了spring boot在分布式系統開發和雲應用開發等方面的應用以及使用微服務建構高可用的服務平台;第三部分(第10~12章)對spring boot的程式加載、自動配置和資料管理的實作原理,以及spring cloud的配置管理、發現服務和負載均衡服務等實作原理進行了深入的剖析。

本書章節編排

第1章為spring boot入門,介紹開發環境的搭建和開發工具的選擇及安裝配置,并使用一個非常簡單的執行個體,說明如何輕易地使用spring boot開發架構。

第2章使用spring boot架構示範了以不同于以往的方式,以及如何輕易地使用資料庫,并實際示範使用mysql、mongodb、redis和neo4j等資料庫。

第3章使用thymeleaf模闆結合一些流行的javascript插件,介紹了使用spring boot進行界面設計的方法和技巧。

第4章對使用spring boot提高傳統關系型資料庫的性能方面做了一些探讨和嘗試,并擴充了使用jpa資源庫的功能。

第5章介紹了如何使用spring boot結合spring security進行安全設計,包括登入認證和角色管理、權限管理等内容。

第6章介紹如何使用spring security結合oauth2進行sso(single sign on)的設計,并示範如何在分布式應用系統中使用認證授權和安全管理的功能。

第7章介紹如何使用spring boot架構結合分布式檔案系統fastdfs,并使用定制方式和富文本編輯器的方式示範了使用圖檔上傳和建立本地圖檔庫的方法。

第8章介紹雲應用開發,包括配置管理、發現服務和監控服務的使用,以及如何使用動态路由和斷路器的功能,建立高可用的微服務應用。

第9章介紹如何使用docker引擎和docker-compose工具來釋出應用和管理服務,以及如何建構一個高性能的服務平台和怎樣使用docker實施負載均衡。

第10章分析了spring boot的應用程式加載和自動配置原理,以及如何以改造加載配置的方式來提高應用的性能。

第11章分析了spring boot使用資料庫的實作原理,并示範怎樣利用一些技術手段提高和擴充通路資料庫的功能。

第12章簡要分析了微服務中配置管理、發現服務和負載均衡服務的實作原理和部分核心源代碼,并使用一個執行個體說明配置管理中分布式消息的實作機制和原理。

附錄a~附錄d介紹了neo4j、mongodb、redis、rabbitmq等伺服器的安裝、配置和基本使用方法。

讀者對象

本書适于所有java程式設計語言開發人員,所有對spring boot感興趣并希望使用spring boot開發架構進行開發的人員,已經使用過spring boot架構但希望更好地使用spring boot的開發人員,以及系統設計師、架構師等設計人員。同時,本書對運維人員和dba等也具有一定的參考價值。

執行個體代碼

本書的執行個體代碼可以通過https://github.com/chenfromsz?tab=repositories檢視和下載下傳,推薦根據每章的提示使用intellij idea通過github檢出各章的執行個體工程,這樣可以保留原來工程的配置,并且能夠直接使用。

回報與勘誤

讀者如有回報意見可以通過https://github.com/chenfromsz/correct/issues發起新話題與作者進行互動,在這也可能會釋出一些勘誤資訊,以便糾正不可避免的錯誤。

緻謝

首先,非常感謝華陽信通公司,雖然本書的編寫過程大都在業餘時間完成,但是公司強大的平台給本書的執行個體提供了更加友善的分享、驗證和測試條件。同時在本書的編寫過程中,也得到了我們的開發團隊和衆多朋友的大力支援和幫助,在此表示衷心的感謝!最後感謝華章公司的楊福川和李藝,他們在本書編輯的過程中,提出了一些寶貴而有益的建議,并為本書的出版做了許多工作。

由于時間倉促和水準有限,書中難免出現一些纰漏或不正确的地方,敬請大家批評指正!

contents 目  錄

前 言

第一部分 基礎應用開發

第1章 spring boot入門  3

1.1 配置開發環境  3

1.1.1 安裝jdk  3

1.1.2 安裝interllij idea  4

1.1.3 安裝apache maven  4

1.1.4 安裝git用戶端  5

1.2 建立項目工程  8

1.2.1 使用maven建立項目  8

1.2.2 使用spring initializr建立項目  11

1.3 使用spring boot  14

1.3.1 maven依賴管理  14

1.3.2 一個簡單的執行個體  17

1.4 運作與釋出  18

1.4.1 在idea環境中運作  18

1.4.2 将應用打包釋出  19

1.5 關于spring boot配置  22

1.6 小結  23

第2章 在spring boot中使用資料庫  24

2.1 使用mysql  24

2.1.1 mysql依賴配置  25

2.1.2 實體模組化  25

2.1.3 實體持久化  27

2.1.4 mysql測試  29

2.2 使用redis  33

2.2.1 redis依賴配置  33

2.2.2 建立redis服務類  34

2.2.3 redis測試  36

2.3 使用mongodb  38

2.3.1 mongodb依賴配置  38

2.3.2 文檔模組化  39

2.3.3 文檔持久化  40

2.3.4 mongodb測試  41

2.4 使用neo4j  43

2.4.1 neo4j依賴配置  43

2.4.2 節點和關系實體模組化  43

2.4.3 節點實體持久化  45

2.4.4 neo4j測試  46

2.5 小結  49

第3章 spring boot界面設計  50

3.1 模型設計  50

3.1.1 節點實體模組化  51

3.1.2 關系實體模組化  51

3.1.3 分頁查詢設計  52

3.2 控制器設計  53

3.2.1 建立控制器  53

3.2.2 檢視控制器  53

3.2.3 修改控制器  54

3.2.4 删除控制器  55

3.2.5 分頁查詢控制器  55

3.3 使用thymeleaf模闆  56

3.3.1 thymeleaf配置  56

3.3.2 thymeleaf功能簡介  57

3.4 視圖設計  60

3.4.1 清單視圖設計  60

3.4.2 建立視圖設計  64

3.4.3 檢視視圖設計  68

3.4.4 修改視圖設計  70

3.4.5 删除視圖設計  72

3.5 運作與釋出  73

3.6 小結  74

第4章 提高資料庫通路性能  75

4.1 使用druid  75

4.1.1 配置druid依賴  76

4.1.2 關于xml配置  76

4.1.3 druid資料源配置  77

4.1.4 開啟監控功能  78

4.2 擴充jpa功能  80

4.2.1 擴充jpa接口  81

4.2.2 裝配自定義的擴充接口  83

4.2.3 使用擴充接口  85

4.3 使用redis做緩存  86

4.3.1 使用spring cache注解  86

4.3.2 使用redistemplate  88

4.4 web應用子產品  91

4.4.1 引用資料管理子產品  91

4.4.2 web應用配置  92

4.5 運作與釋出  94

4.6 小結  95

第5章 spring boot安全設計  96

5.1 依賴配置管理  96

5.2 安全政策配置  97

5.2.1 權限管理規則  98

5.2.2 登入成功處理器  99

5.2.3 防攻擊政策  100

5.2.4 記住登入狀态  102

5.3 登入認證設計  103

5.3.1 使用者實體模組化  103

5.3.2 使用者身份驗證  104

5.3.3 登入界面設計  106

5.3.4 驗證碼驗證  108

5.4 權限管理設計  109

5.4.1 權限管理配置  109

5.4.2 權限管理過濾器  110

5.4.3 權限配置資料總管  111

5.4.4 權限管理決斷器  112

5.5 根據權限設定連結  113

5.6 運作與釋出  116

5.6.1 系統初始化  116

5.6.2 系統運作與釋出  118

5.7 小結  119

第二部分 分布式應用開發

第6章 spring boot sso  123

6.1 子產品化設計  123

6.2 登入認證子產品  124

6.2.1 使用oauth2  124

6.2.2 建立數字證書  125

6.2.3 認證服務端配置  125

6.3 安全配置子產品  128

6.4 sso用戶端  129

6.4.1 用戶端配置  129

6.4.2 登入登出設計  130

6.5 共享資源服務  132

6.5.1 提供共享資源接口  133

6.5.2 使用共享資源  134

6.5.3 查詢登入使用者的詳細資訊  135

6.6 運作與釋出  136

6.7 小結  138

第7章 使用分布式檔案系統  139

7.1 fastdfs安裝  139

7.1.1 下載下傳安裝包  141

7.1.2 安裝服務  141

7.1.3 tracker server配置  142

7.1.4 storage server配置  145

7.1.5 啟動服務  148

7.1.6 用戶端測試  148

7.2 fastfds用戶端  149

7.2.1 用戶端配置  150

7.2.2 用戶端服務類  150

7.3 使用定制方式上傳圖檔  151

7.3.1 實體模組化  151

7.3.2 上傳圖檔  152

7.4 使用富文本編輯器上傳圖檔  156

7.4.1 使用富文本編輯器  156

7.4.2 實作檔案上傳  157

7.5 使用本地檔案庫  158

7.5.1 本地檔案庫模組化  159

7.5.2 檔案儲存方法  159

7.5.3 檔案庫管理  161

7.6 運作與釋出  163

7.7 小結  164

第8章 雲應用開發  165

8.1 使用配置管理  166

8.1.1 建立配置管理伺服器  167

8.1.2 使用配置管理的用戶端  168

8.1.3 實作線上更新  171

8.1.4 更新所有用戶端的配置  172

8.2 使用發現服務  174

8.2.1 建立發現伺服器  174

8.2.2 使用發現服務的用戶端配置  175

8.2.3 發現伺服器測試  175

8.3 使用動态路由和斷路器  176

8.3.1 依賴配置  176

8.3.2 共享rest資源  177

8.3.3 通過路由通路rest資源  180

8.3.4 使用斷路器功能  182

8.3.5 路由器和斷路器測試  183

8.4 使用監控服務  184

8.4.1 建立監控伺服器  184

8.4.2 監控伺服器測試  185

8.5 運作與釋出  187

8.6 小結  187

第9章 建構高性能的服務平台  188

9.1 使用docker  188

9.1.1 docker安裝  189

9.1.2 docker常用指令  190

9.1.3 使用docker釋出服務  191

9.2 建立和管理一個高性能的服務體系  194

9.2.1 安裝docker-compose  194

9.2.2 docker-compose常用指令  195

9.2.3 使用docker-compose管理服務  195

9.3 使用docker的其他負載均衡實施方法  199

9.3.1 使用nginx與docker建構負載均衡服務  199

9.3.2 阿裡雲的負載均衡設計執行個體  199

9.4 小結  201

第三部分 核心技術源代碼分析

第10章 spring boot自動配置實作原理  205

10.1 spring boot主程式的功能  205

10.1.1 springapplication的run方法  206

10.1.2 建立應用上下文  207

10.1.3 自動加載  208

10.2 spring boot自動配置原理  209

10.2.1 自動配置的即插即用原理  210

10.2.2 自動配置的約定優先原理  211

10.3 提升應用的性能  211

10.3.1 更改加載配置的方式  212

10.3.2 将tomcat換成jetty  214

10.4 性能對照測試  215

10.5 小結  217

第11章 spring boot資料通路實作原理  218

11.1 連接配接資料源的源代碼分析  218

11.1.1 資料源類型和驅動  219

11.1.2 支援的資料庫種類  220

11.1.3 與資料庫伺服器建立連接配接  221

11.2 資料存取功能實作原理  222

11.2.1 實體模組化源代碼分析  222

11.2.2 持久化實作原理  225

11.3 擴充資料存取的功能  227

11.3.1 擴充jpa功能  227

11.3.2 擴充neo4j功能  228

11.4 小結  230

第12章 微服務核心技術實作原理  231

12.1 配置管理實作原理  232

12.1.1 線上更新流程  232

12.1.2 更新消息的分發原理  233

12.2 發現服務源代碼剖析  235

12.2.1 服務端的服務注冊功能  236

12.2.2 用戶端注冊和提取服務清單  238

12.3 負載均衡源代碼剖析  240

12.4 分布式消息實作原理示範  244

12.4.1 消息生産者  244

12.4.2 消息消費者  245

12.5 小結  247

附錄a 安裝neo4j  248

附錄b 安裝mongodb  251

附錄c 安裝redis  253

附錄d 安裝rabbitmq  256

結束語  262

第一部分 part 1

基礎應用開發

第1章 spring boot入門

第2章 在spring boot中使用資料庫

第3章 spring boot界面設計

第4章 提高資料庫通路性能

第5章 spring boot安全設計

這一部分從搭建開發環境,簡單入門,到使用資料庫、界面設計、安全管理等一系列内容,介紹了使用spring boot架構進行基礎應用開發的方法。

第1章介紹了開發環境的搭建和開發工具的選擇和安裝,并以一個非常簡單的執行個體,示範了如何使用spring boot架構建立工程和釋出應用。

第2章介紹了如何用spring boot特有的方式,使用目前流行的資料庫:mysql、redis、mongodb、neo4j等。

第3章介紹如何使用thymeleaf模闆結合一些流行的javascript插件,設計應用界面。

第4章使用druid資料庫連接配接池和redis做緩存來嘗試提升關系型資料庫的通路性能,并擴充了jpa的資源庫功能。

第5章在spring boot中使用spring security為應用系統進行安全設計,實作了登入認證和權限管理方面的功能。

第1章

spring boot入門

在使用spring boot架構進行各種開發體驗之前,要先配置好開發環境。首先安裝jdk,然後選擇一個開發工具,如eclipse ide和intellij idea(以下簡稱idea)都是不錯的選擇。對于開發工具的選擇,本書極力推薦使用idea,因為它為spring boot提供了許多更好和更貼切的支援,本書的執行個體都是使用idea建立的。同時,還需要安裝apache maven和git用戶端。所有這些都準備好之後,我們就能開始使用spring 

boot了。

1.1 配置開發環境

下面的開發環境配置主要以使用windows作業系統為例,如果你使用的是其他作業系統,請對照其相關配置進行操作。

1.1.1 安裝jdk

jdk(java se development kit)需要1.8及以上版本,可以從java的官網http://www.oracle.com/technetwork/java/javase/downloads/index.html下載下傳安裝包。如果通路官網速度慢的話,也可以通過百度搜尋jdk,然後在百度軟體中心下載下傳符合你的windows版本和配置的jdk1.8安裝包。

安裝完成後,配置環境變量java_home,例如,使用路徑d:\program files\java\jdk1.8.0_25(如果你安裝的是這個目錄的話)。java_home配置好之後,将%java_home%\bin加入系統的環境變量path中。完成後,打開一個指令行視窗,輸入指令java–version,如果能正确輸出版本号則說明安裝成功了。輸出版本的資訊如下:

c:\users\alan>java-version

java version "1.8.0_25"

java(tm) se runtime environment (build 1.8.0_25-b18)

java hotspot(tm) 64-bit server vm (build 25.25-b02, mixed mode)

1.1.2 安裝interllij idea

idea需要14.0以上的版本,可以從其官網http://www.jetbrains.com/下載下傳免費版,本書的執行個體是使用idea14.1.15版本開發的。idea已經包含maven插件,版本是3.0.5,這已經能夠适用我們開發的要求。安裝完成後,打開idea,将顯示如圖1-1所示的歡迎界面,在這裡可以看到idea的版本号。

圖1-1 interllij idea歡迎界面

1.1.3 安裝apache maven

為了能夠在指令行視窗中使用maven來管理工程,可以安裝一個maven管理工具。通過maven的官網http://maven.apache.org/download.cgi下載下傳3.0.5以上的版本,下載下傳完後解壓縮即可,例如,解壓到d:盤上是不錯的做法,然後将maven的安裝路徑(如d:\apache-maven-3.2.3\bin)也加入windows的環境變量path中。安裝完成後,在指令行視窗中執行指令:mvn–v,将輸出如下的版本資訊以及系統的一些環境資訊。

c:\users\alan>mvn-v

apache maven 3.2.3 (33f8c3e1027c3ddde99d3cdebad2656a31e8fdf4; 2014-08-12t04:58:1

0+08:00)

maven home: d:\apache-maven-3.2.3\bin\..

java version: 1.8.0_25, vendor: oracle corporation

java home: d:\program files\java\jdk1.8.0_25\jre

default locale: zh_cn, platform encoding: gbk

os name: "windows 7", version: "6.1", arch: "amd64", family: "dos"

建議更改idea中maven資源庫的存放路徑,可以先在maven安裝路徑中建立一個資源庫目錄,如repository。然後打開maven的配置檔案,即安裝目錄conf中的settings.xml,找到下列代碼,将路徑更改為repository所在的位置,并儲存在注釋符下面。

例如找到下列代碼行:

<localrepository>/path/to/local/repo</localrepository>

複制出來改為如下所示:

<localrepository>d:\apache-maven-3.2.3\repository</localrepository>

改好後可以拷貝一份settings.xml放置在${user.home}/.m2/下面,這樣做可以不用修改idea的maven這個配置。在圖1-2所示的maven配置界面中,user settings file保持了預設位置,local repository使用了上面設定的路徑d:\apache-maven-3.2.3\repository,而maven程式還是使用了idea自帶的版本。

1.1.4 安裝git用戶端

由于本書的執行個體工程都存放在github(https://github.com/)中,是以還需要在github中免費注冊一個使用者(可以通過e-mail直接注冊免費使用者),以友善在idea中從github檢出本書的執行個體工程。當然,如果不想注冊,通過普通下載下傳的方法也能取得執行個體工程的源代碼。github是世界級的代碼庫伺服器,如果你願意,也可以将它作為你的代碼庫伺服器,在這裡還可以搜尋到全世界的開發者分享出來的源程式。圖1-3是打開github的首頁。

圖1-2 maven設定

圖1-3 github首頁

idea還需要git用戶端程式的支援。可以從其官網https://git-scm.com/download/下載下傳git用戶端安裝包。安裝非常簡單,按提示單擊“下一步”并選擇好安裝路徑即可。安裝完成後,在windows的資料總管中,單擊滑鼠右鍵彈出的菜單中将會多出如下幾個選擇菜單:

git init here

git gui

git bash

其中git bash是一個帶有unix指令的指令行視窗,在這裡可以執行一些git指令,用來送出或者檢出項目。

在idea中對git的設定,隻要指定git.exe執行檔案的位置即可。圖1-4是idea中git用戶端的配置,其中git的路徑被設定在d:\program files\git\bin\git.exe中,這主要由安裝git用戶端的位置而定。

圖1-4 git設定

如果已經在github中注冊了使用者,即可以打開如圖1-5所示的github配置,輸入使用者名和密碼,然後單擊test按鈕,如果設定正确的話将會傳回連接配接成功的提示。

圖1-5 github配置

上面idea的一些設定界面都可以單擊工具欄上的settings按鈕打開,打開file菜單,選擇settings同樣也可以打開。

1.2 建立項目工程

現在,可以嘗試使用idea來建立一個項目工程。如果是第一次打開idea,可以選擇create new project建立一個新工程。如果已經打開了idea,在file菜單中選擇new project,也能打開new project對話框,如圖1-6所示。使用idea建立一個spring boot項目有很多方法,這裡隻介紹使用maven和spring initializr這兩種方法來建立一個新項目。一般使用maven來建立一個項目,因為這樣更容易按我們的要求配置一個項目。

1.2.1 使用maven建立項目

使用maven建立一個項目主要有以下三個步驟。

圖1-6 建立一個maven項目

1.?選擇項目類型

在圖1-6中的project sdk下拉清單框中選擇前面安裝的java 1.8,如果下拉清單框中不存在java 1.8,可以單擊new按鈕,找到安裝java的位置,選擇它。然後在左面側邊欄的項目類型中,選擇maven項目,即可使用maven作為項目的管理工具。至于maven中的archetype,因為我們并不打算使用其中任何一種類型,是以不用勾選,然後單擊next進入下一步。

2.?輸入groupid和artifactid

在groupid輸入框中輸入“springboot.example”,在artifactid輸入框中輸入“spring-boot-hello”,version輸入框中保持預設值,如圖1-7所示,單擊next進入下一步。

3.?指定項目名稱和存放路徑

在project location編輯框中選擇和更改存放路徑,在project name輸入框中輸入與artifactid相同的項目名稱:“spring-boot-hello”,如圖1-8所示。

單擊finish,完成項目建立,這樣将在目前視窗中打開一個新項目,如圖1-9所示。其中,在工程根目錄中生成了一個pom.xml,即maven的項目對象模型(project object model),并生成了源代碼目錄java、資源目錄resources和測試目錄test等,即生成了一個項目的一些初始配置和目錄結構。

圖1-7 輸入groupid和artifactid

圖1-8 指定項目名稱和存放路徑

圖1-9 初始建立的項目

下一節将使用這個項目工程來建立第一個使用spring boot開發架構的應用執行個體。

1.2.2 使用spring initializr建立項目

建立一個spring boot項目,也可以使用spring initializr的方式,這種方式很簡單,如圖1-10所示。注意initializr service url為https://start.spring.io,這将會連接配接網絡,以查詢spring boot的目前可用版本群組件清單。使用這種方式建立項目大體上也需要三個步驟。

1.?選擇類型

可以使用預設選項,注意type為maven project,java version為1.8,packaging為jar,如圖1-11所示。單擊next進入下一步。

2.?選擇spring boot版本群組件

選擇spring boot版本和spring boot元件,例如,在spring boot version中選擇1.3.5,并勾選web項目元件,如圖1-12所示,然後單擊next進入下一步。

圖1-10 建立一個spring boot項目

圖1-11 選擇項目類型

圖1-12 選擇版本群組件

3.?輸入項目名稱

選擇存放路徑後輸入項目名稱,如圖1-13所示,這裡使用demo作為項目的名稱。

圖1-13 輸入項目名稱

單擊finish,将建立一個初始化項目,如圖1-14所示。這個項目不但有完整的目錄結構,還有一個完整的maven配置,并且生成了一個預設的主程式,幾乎所有的準備工作都已經就緒,并且可以立即運作起來(雖然沒有提供任何可用的服務)。這也是spring boot引以為傲的地方,即建立一個應用可以不用編寫任何代碼,隻管運作即可。

圖1-14 使用spring initializr建立的初始項目

1.3 使用spring boot

任何應用的開發都需要對項目的建立、運作和釋出等進行管理,使用spring boot架構進行開發,可以選擇使用maven或gradle等項目管理工具。在這裡我們使用的是maven。

1.3.1 maven依賴管理

使用maven,通過導入spring boot的starter子產品,可以将許多程式依賴包自動導入工程中。使用maven的parent pom,還可以更容易地管理依賴的版本和使用預設的配置,工程中的子產品也可以很友善地繼承它。例如,使用1.2.1節建立的工程,修改pom.xml檔案,使用如代碼清單1-1所示的簡單maven配置,基本上就能為一個使用spring boot開發架構的web項目開發提供所需的相關依賴。

代碼清單1-1 spring boot web基本依賴配置

<?xml version="1.0" encoding="utf-8"?>

<project xmlns="http://maven.apache.org/pom/4.0.0"

        xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"

        xsi:schemalocation="http://maven.apache.org/pom/4.0.0 

            http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelversion>4.0.0</modelversion>

    <groupid>springboot.example</groupid>

    <artifactid>spring-boot-hello</artifactid>

    <version>1.0-snapshot</version>

    <parent>

        <groupid>org.springframework.boot</groupid>

        <artifactid>spring-boot-starter-parent</artifactid>

        <version>1.3.2.release</version>

    </parent>

    <dependencies>

        <dependency>

            <groupid>org.springframework.boot</groupid>

            <artifactid>spring-boot-starter-web</artifactid>

        </dependency>

    </dependencies>

</project>

這裡隻使用了一個依賴配置spring-boot-starter-web和一個parent配置spring-boot-starter-parent,在工程的外部庫(external libraries)清單中,它自動引入的依賴包如代碼清單1-2所示。

代碼清單1-2 maven加載的依賴清單

<orderentry type="library" name="maven: org.springframework.boot:spring-boot-starter-web:1.3.2.release" level="project" />

<orderentry type="library" name="maven: org.springframework.boot:spring-boot-starter:1.3.2.release" level="project" />

<orderentry type="library" name="maven: org.springframework.boot:spring-boot:1.3.2.release" level="project" />

<orderentry type="library" name="maven: org.springframework.boot:spring-boot-autoconfigure:1.3.2.release" level="project" />

<orderentry type="library" name="maven: org.springframework.boot:spring-boot-starter-logging:1.3.2.release" level="project" />

<orderentry type="library" name="maven: ch.qos.logback:logback-classic:1.1.3" level="project" />

<orderentry type="library" name="maven: ch.qos.logback:logback-core:1.1.3" level="project" />

<orderentry type="library" name="maven: org.slf4j:slf4j-api:1.7.13" level="project" />

<orderentry type="library" name="maven: org.slf4j:jcl-over-slf4j:1.7.13" level="project" />

<orderentry type="library" name="maven: org.slf4j:jul-to-slf4j:1.7.13" level="project" />

<orderentry type="library" name="maven: org.slf4j:log4j-over-slf4j:1.7.13" level="project" />

<orderentry type="library" name="maven: org.springframework:spring-core:4.2.4.release" level="project" />

<orderentry type="library" scope="runtime" name="maven: org.yaml:snakeyaml:1.16" level="project" />

<orderentry type="library" name="maven: org.springframework.boot:spring-boot-starter-tomcat:1.3.2.release" level="project" />

<orderentry type="library" name="maven: org.apache.tomcat.embed:tomcat-embed-core:8.0.30" level="project" />

<orderentry type="library" name="maven: org.apache.tomcat.embed:tomcat-embed-el:8.0.30" level="project" />

<orderentry type="library" name="maven: org.apache.tomcat.embed:tomcat-embed-logging-juli:8.0.30" level="project" />

<orderentry type="library" name="maven: org.apache.tomcat.embed:tomcat-embed-websocket:8.0.30" level="project" />

<orderentry type="library" name="maven: org.springframework.boot:spring-boot-starter-validation:1.3.2.release" level="project" />

<orderentry type="library" name="maven: org.hibernate:hibernate-validator:5.2.2.final" level="project" />

<orderentry type="library" name="maven: javax.validation:validation-api:1.1.0.final" level="project" />

<orderentry type="library" name="maven: org.jboss.logging:jboss-logging:3.3.0.final" level="project" />

<orderentry type="library" name="maven: com.fasterxml:classmate:1.1.0" level="project" />

<orderentry type="library" name="maven: com.fasterxml.jackson.core:jackson-databind:2.6.5" level="project" />

<orderentry type="library" name="maven: com.fasterxml.jackson.core:jackson-annotations:2.6.5" level="project" />

<orderentry type="library" name="maven: com.fasterxml.jackson.core:jackson-core:2.6.5" level="project" />

<orderentry type="library" name="maven: org.springframework:spring-web:4.2.4.release" level="project" />

<orderentry type="library" name="maven: org.springframework:spring-aop:4.2.4.release" level="project" />

<orderentry type="library" name="maven: aopalliance:aopalliance:1.0" level="project" />

<orderentry type="library" name="maven: org.springframework:spring-beans:4.2.4.release" level="project" />

<orderentry type="library" name="maven: org.springframework:spring-context:4.2.4.release" level="project" />

<orderentry type="library" name="maven: org.springframework:spring-webmvc:4.2.4.release" level="project" />

<orderentry type="library" name="maven: org.springframework:spring-expression:4.2.4.release" level="project" />

在工程的外部庫清單中,spring boot已經導入了整個springframework依賴,以及autoconf?igure、logging、slf4j、jackson、tomcat插件等,所有這些都是一個web項目可能需要用到的東西(包括你已經考慮到的和沒有考慮到的),它真是一個聰明的助手。

1.3.2 一個簡單的執行個體

spring boot的官方文檔中提供了一個最簡單的web執行個體程式,這個執行個體隻使用了幾行代碼,如代碼清單1-3所示。雖然簡單,但實際上這已經可以算作是一個完整的web項目了。

代碼清單1-3 spring boot簡單執行個體

package springboot.example;

import org.springframework.boot.springapplication;

import org.springframework.boot.autoconfigure.springbootapplication;

import org.springframework.web.bind.annotation.requestmapping;

import org.springframework.web.bind.annotation.restcontroller;

@springbootapplication

@restcontroller

public class application {

    @requestmapping("/")

    string home() {

        return "hello";

    }

    public static void main(string[] args) {

        springapplication.run(application.class, args);

}

這個簡單執行個體,首先是一個spring boot應用的程式入口,或者叫作主程式,其中使用了一個注解@springbootapplication來标注它是一個spring boot應用,main方法使它成為一個主程式,将在應用啟動時首先被執行。其次,注解@restcontroller同時标注這個程式還是一個控制器,如果在浏覽器中通路應用的根目錄,它将調用home方法,并輸出字元串:hello。

1.4 運作與釋出

本章執行個體工程的完整代碼可以使用idea直接從github的https://github.com/chen-fromsz/spring-boot-hello.git中檢出,如圖1-15所示,單擊clone按鈕将整個項目複制到本地。

圖1-15 檢出執行個體工程

1.4.1 在idea環境中運作

在idea中打開run菜單,選擇edit conf?iguration打開run/debug conf?igurations對話框,在配置界面的左邊側邊欄中選擇增加一個application或spring boot配置項目,然後在工作目錄中選擇工程所在的根目錄,主程式選擇代碼清單1-3建立的類:springboot.example.application,并将配置儲存為hello,如圖1-16所示。

然後選擇run或debug運作hello配置項目。如果啟動成功,将在控制台中輸出類似如下資訊:

"d:\program files\java\jdk1.8.0_25\bin\java"

.....

 :: spring boot ::        (v1.3.2.release)

......

starting servlet engine: apache tomcat/8.0.30

tomcat started on port(s): 8080 (http)

從上面的輸出中可以看出,tomcat預設開啟了8080端口。要通路這個應用提供的服務,可以在浏覽器的位址欄中輸入http://localhost:8080/。這樣就可以看到我們期望的輸出字元:hello。

圖1-16 spring boot應用配置

1.4.2 将應用打包釋出

上面操作示範了在idea環境中如何運作一個應用。如果我們想把應用釋出出去,需要怎麼做呢?可以将代碼清單1-1中的maven配置增加一個釋出插件來實作。如代碼清單1-4所示,增加了一個打包插件:spring-boot-maven-plugin,并增加了一行打包的配置:<packaging>jar</packaging>,這行配置指定将應用工程打包成jar檔案。

代碼清單1-4 包含打包插件的maven配置

    <packaging>jar</packaging>

    <build>

        <plugins>

            <plugin>

                <groupid>org.springframework.boot</groupid>

                <artifactid>spring-boot-maven-plugin</artifactid>

                <executions>

                    <execution>

                        <goals>

                            <goal>repackage</goal>

                        </goals>

                    </execution>

                </executions>

            </plugin>

        </plugins>

    </build>

這樣就可以在idea中增加一個打包的配置,打開run/debug conf?igurations對話框,選擇增加配置一個maven打包項目,在工作目錄中選擇工程所在根目錄,在指令行中輸入package,并将配置儲存為mvn,如圖1-17所示。

運作mvn打包項目,就可以将執行個體工程打包,打包的檔案将輸出在工程的target目錄中。

如果已經按照1.1.3節的說明安裝了maven,也可以直接使用maven的指令打包。打開一個指令行視窗,将路徑切換到工程根目錄中,直接在指令行輸入mvn package,同樣也能将項目打包成jar檔案。執行結果如下:

圖1-17 maven打包配置

[info] --- maven-jar-plugin:2.5:jar (default-jar) @ spring-boot-hello ---

[info] building jar: e:\ideworkspace\spring-boot-hello\target\spring-boot-hello-

1.0-snapshot.jar

[info]

[info] --- spring-boot-maven-plugin:1.3.2.release:repackage (default) @ spring-b

oot-hello ---

[info] ------------------------------------------------------------------------

[info] build success

[info] total time: 21.450 s

[info] finished at: 2016-05-08t16:54:44+08:00

[info] final memory: 23m/118m

打包成功後,在工程的target目錄中将會生成jar檔案spring-boot-hello-1.0-snapshot.jar。在指令行視窗中切換到target目錄中,運作如下指令,就能啟動應用。

java -jar spring-boot-hello-1.0-snapshot.jar

如果希望按照傳統的做法,将工程釋出成war檔案,應當将代碼清單1-4的maven配置<packaging>jar</packaging>改成<packaging>war</packaging>,這樣就可以打包成war檔案。打包完成後将war檔案放置在tomcat的webapp路徑中,啟動tomcat就能自動運作程式。

這裡需要注意的是,如果自主使用tomcat運作應用,在安裝jdk時必須配置java_home環境變量,同時jdk要求1.8以上的版本,tomcat必須是8.0以上的版本。

我更加喜歡打包成jar,然後使用spring boot的嵌入插件tomcat運作應用。本書所有執行個體都可以打包成jar直接運作。即使對于一個包含很多頁面、圖檔、腳本等資源的複雜應用系統,這種方法也是可行的,并且打包成jar,更友善項目釋出在docker上運作,這些将在後面的章節中詳細介紹。

1.5 關于spring boot配置

關于spring boot配置,可以在工程的resources檔案夾中建立一個application.properties或application.yml檔案,這個檔案會被釋出在classpath中,并且被spring boot自動讀取。這裡推薦使用application.yml檔案,因為它提供了結構化及其嵌套的格式,例如,可以按如下所示配置上面的工程,将預設端口改為80,并且将tomcat的字元集定義為utf-8。

server:

    port: 80

    tomcat:

        uri-encoding: utf-8

如果要使用application.properties檔案,上面的配置就要改成如下所示的樣子,其結果完全相同。

server.port = 80

server.tomcat.uri-enconding = utf-8

使用這個配置檔案可以直接使用spring boot預定義的一些配置參數,關于其他配置參數的詳細說明和描述可以檢視官方的文檔說明:https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html。在後面的開發中将在用得到的地方選擇使用這些預定義的配置參數。即使沒有預定義的配置參數可用,也能很容易地按照應用的需要自定義一些配置參數,這将在後續的章節中詳細介紹。

1.6 小結

本章主要介紹了spring boot開發環境的搭建,以及一些開發工具的安裝配置,内容難免有點枯燥。然後建立并運作一個非常簡單的執行個體工程,讓性急的讀者一睹spring boot的芳容。

本章執行個體工程隻是使用spring boot架構進行開發的非常簡單的入門指引。因為spring boot開發架構是一個非常輕量級的開發架構,是以也有人把它叫作微架構,從入門指引中可以看出,使用spring boot架構開發應用不但入門容易,而且其蘊藏的無比強大的功能,使開發過程也變得更加容易。

下面,讓我們使用spring boot架構進行一些更加有趣的開發吧。這一章隻是小試牛刀而已,在後續章節中将使用spring boot架構來開始一些真正的開發。

第2章

在spring boot中使用資料庫

使用資料庫是開發基本應用的基礎。借助于開發架構,我們已經不用編寫原始的通路資料庫的代碼,也不用調用jdbc(java data base connectivity)或者連接配接池等諸如此類的被稱作底層的代碼,我們将在進階的層次上通路資料庫。而spring boot更是突破了以前所有開發架構通路資料庫的方法,在前所未有的更加進階的層次上通路資料庫。因為spring boot包含一個功能強大的資源庫,為使用spring boot的開發者提供了更加簡便的接口進行通路。

本章将介紹怎樣使用傳統的關系型資料庫,以及近期一段時間異軍突起的nosql(not only sql)資料庫。

本章的執行個體工程使用了分子產品的方式建構,各子產品的定義如表2-1所示。

表2-1 執行個體工程子產品定義

項  目 工  程 功  能

mysql子產品 mysql 使用mysql

redis子產品 redis 使用redis

mongodb子產品 mongodb 使用mongodb

neo4j子產品 neo4j 使用neo4j

2.1 使用mysql

對于傳統關系型資料庫來說,spring boot使用jpa(java persistence api)資源庫來實作對資料庫的操作,使用mysql也是如此。簡單地說,jpa就是為pojo(plain ordinary java object)提供持久化的标準規範,即将java的普通對象通過對象關系映射(object-relational mapping,orm)持久化到資料庫中。

2.1.1 mysql依賴配置

為了使用jpa和mysql,首先在工程中引入它們的maven依賴,如代碼清單2-1所示。其中,指定了在運作時調用mysql的依賴。

代碼清單2-1 jpa和mysql依賴配置

<dependencies>

    <dependency>

        <artifactid>spring-boot-starter-data-jpa</artifactid>

    </dependency>

        <groupid>mysql</groupid>

        <artifactid>mysql-connector-java</artifactid>

        <scope>runtime</scope>

</dependencies>

2.1.2 實體模組化

首先建立一些普通對象,用來與資料庫的表建立映射關系,接着示範如何使用jpa對資料庫進行增删查改等存取操作。

假如現在有三個實體:部門、使用者和角色,并且它們具有一定的關系,即一個使用者隻能隸屬于一個部門,一個使用者可以擁有多個角色。它們的關系模型如圖2-1所示。

圖2-1 mysql實體-關系模型示例

spring boot的實體模組化與使用spring架構時的定義方法一樣,同樣比較友善的是使用了注解的方式來實作。

部門實體的模組化如代碼清單2-2所示,其中注解@table指定關聯的資料庫的表名,注解@id定義一條記錄的唯一辨別,并結合注解@generatedvalue将其設定為自動生成。部門實體隻有兩個字段:id和name。程式中省略了getter和setter方法的定義,這些方法可以使用idea的自動生成工具很友善地生成。

代碼清單2-2 部門實體模組化

@entity

@table(name = "deparment")

public class deparment {

    @id

    @generatedvalue(strategy = generationtype.identity)

    private long id;

    private string name;

    public deparment() {

        ……

使用者實體包含三個字段:id、name和createdate,使用者實體模組化如代碼清單2-3所示。其中注解@manytoone定義它與部門的多對一關系,并且在資料庫表中用字段did來表示部門的id,注解@manytomany定義與角色實體的多對多關系,并且用中間表user_role來存儲它們各自的id,以表示它們的對應關系。日期類型的資料必須使用注解@datetimeformat來進行格式化,以保證它在存取時能提供正确的格式,避免儲存失敗。注解@jsonbackreference用來防止關系對象的遞歸通路。

代碼清單2-3 使用者實體模組化

@table(name = "user")

public class user implements java.io.serializable{

    @datetimeformat(pattern = "yyyy-mm-dd hh:mm:ss")

    private date createdate;

    @manytoone

    @joincolumn(name = "did")

    @jsonbackreference

    private department deparment;

    @manytomany(cascade = {}, fetch = fetchtype.eager)

    @jointable(name = "user_role",

            joincolumns = {@joincolumn(name = "user_id")},

            inversejoincolumns = {@joincolumn(name = "roles_id")})

    private list<role> roles;

    public user() {

……

角色實體模組化比較簡單,隻要按設計的要求,定義id和name字段即可,當然同樣必須保證id的唯一性并将其設定為自動生成。角色實體的模組化如代碼清單2-4所示。

代碼清單2-4 角色實體模組化

@table(name = "role")

public class role implements java.io.serializable{

    public role() {

2.1.3 實體持久化

通過上面三個實體的定義,實作了使用java的普通對象(pojo)與資料庫表建立映射關系(orm),接下來使用jpa來實作持久化。

使用者實體使用jpa進行持久化的例子如代碼清單2-5所示。它是一個接口,并繼承于jpa資源庫jparepository接口,使用注解@repository将這個接口也定義為一個資源庫,使它能被其他程式引用,并為其他程式提供存取資料庫的功能。

使用相同的方法,可以定義部門實體和角色實體的資源庫接口。接口同樣繼承于jparepository接口,隻要注意使用的參數是各自的實體對象即可。

代碼清單2-5 使用者實體持久化

@repository

public interface userrepository extends jparepository<user, long> {

這樣就實作存取資料庫的功能了。現在可以對資料庫進行增删查改、進行分頁查詢和指定排序的字段等操作。

或許你還有疑問,我們定義的實體資源庫接口并沒有聲明一個方法,也沒有對接口有任何實作的代碼,甚至連一條sql查詢語句都沒有寫,這怎麼可能?

是的,使用jpa就是可以這麼簡單。我們來看看jpare-pository的繼承關系,你也許會明白一些。如圖2-2所示,jparepository繼承于pagingandsortingrepository,它提供了分頁和排序功能,pagingandsortingrepository繼承于crud-repository,它提供了簡單的增删查改功能。

因為定義的接口繼承于jparepository,是以它傳遞性地繼承上面所有這些接口,并擁有這些接口的所有方法,這樣就不難了解為何它包含那麼多功能了。這些接口提供的一些方法如下:

<s extends t> s save(s var1);

t findone(id var1);

long count();

void delete(id var1);

void delete(t var1);

void deleteall();

page<t> findall(pageable var1);

list<t> findall();

list<t> findall(sort var1);

list<t> findall(iterable<id> var1);

void deleteallinbatch();

t getone(id var1);

jpa還提供了一些自定義聲明方法的規則,例如,在接口中使用關鍵字f?indby、readby、getby作為方法名的字首,拼接實體類中的屬性字段(首個字母大寫),并可選擇拼接一些sql查詢關鍵字來組合成一個查詢方法。例如,對于使用者實體,下列查詢關鍵字可以這樣使用:

and,例如f?indbyidandname(long id,string name);

or,例如f?indbyidorname(long id,string name);

between,例如f?indbycreatedatebetween(date start,date end);

lessthan,例如f?indbycreatedatelessthan(date start);

greaterthan,例如f?indbycreatedategreaterthan(date start);

isnull,例如f?indbynameisnull();

isnotnull,例如f?indbynameisnotnull();

notnull,與isnotnull等價;

like,例如f?indbynamelike(string name);

notlike,例如f?indbynamenotlike(string name);

orderby,例如f?indbynameorderbyidasc(string name);

not,例如f?indbynamenot(string name);

in,例如f?indbynamein(collection<string>namelist);

notin,例如f?indbynamenotin(collection<string>namelist)。

又如下列對使用者實體類自定義的方法聲明,它們都是符合jpa規則的,這些方法也不用實作,jpa将會代理實作這些方法。

user findbynamelike(string name);

user readbyname(string name);

list<user> getbycreatedatelessthan(date star);

2.1.4 mysql測試

現在,為了驗證上面設計的正确性,我們用一個執行個體來測試一下。

首先,增加一個使用jpa的配置類,如代碼清單2-6所示。其中@enabletransac-tionmanagement啟用了jpa的事務管理;@enablejparepositories啟用了jpa資源庫并指定了上面定義的接口資源庫的位置;@entityscan指定了定義實體的位置,它将導入我們定義的實體。注意,在測試時使用的jpa配置類可能與這個配置略有不同,這個配置的一些配置參數是從配置檔案中讀取的,而測試時使用的配置類把一些配置參數都包含在類定義中了。

代碼清單2-6 jpa配置類

@order(ordered.highest_precedence)

@configuration

@enabletransactionmanagement(proxytargetclass = true)

@enablejparepositories(basepackages = "dbdemo.**.repository")

@entityscan(basepackages = "dbdemo.**.entity")

public class jpaconfiguration {

    @bean

    persistenceexceptiontranslationpostprocessor persistenceexceptiontranslationpostprocessor(){

        return new persistenceexceptiontranslationpostprocessor();

其次,在mysql資料庫伺服器中建立一個資料庫test,然後配置一個可以通路這個資料庫的使用者及其密碼。資料庫的表結構可以不用建立,在程式運作時将會按照實體的定義自動建立。如果還沒有建立一個具有完全權限通路資料庫test的使用者,可以在連接配接mysql伺服器的查詢視窗中執行下面指令,這個指令假設你将在本地中通路資料庫。

grant all privileges on test.* to 'root'@'localhost' identified by '12345678';

然後,在spring boot的配置檔案application.yml中使用如代碼清單2-7所示的配置,用來設定資料源和jpa的工作模式。

代碼清單2-7 資料源和jpa配置

spring:

    datasource:

        url: jdbc:mysql:// localhost:3306/test?characterencoding=utf8

        username: root

        password: 12345678

    jpa:

        database: mysql

        show-sql: true

    #hibernate ddl auto (validate|create|create-drop|update)

        hibernate:

            ddl-auto: update

            naming-strategy: org.hibernate.cfg.improvednamingstrategy

        properties:

            hibernate:

                dialect: org.hibernate.dialect.mysql5dialect

配置中将ddl-atuo設定為update,就是使用hibernate來自動更新表結構的,即如果資料表不存在則建立,或者如果修改了表結構,在程式啟動時則執行表結構的同步更新。

最後,編寫一個測試程式,如代碼清單2-8所示。測試程式首先初始化資料庫,建立一個部門,命名為“開發部”,建立一個角色,命名為admin,建立一個使用者,命名為user,同時将它的所屬部門設定為上面建立的部門,并将現有的所有角色都配置設定給這個使用者。然後使用分頁的方式查詢所有使用者的清單,并從查到的使用者清單中,列印出使用者的名稱、部門的名稱和第一個角色的名稱等資訊。

代碼清單2-8 mysql測試程式

@runwith(springjunit4classrunner.class)

@contextconfiguration(classes = {jpaconfiguration.class})

public class mysqltest {

    private static logger logger = loggerfactory.getlogger(mysqltest.class);

    @autowired

    userrepository userrepository;

    departmentrepository departmentrepository;

    rolerepository rolerepository;

    @before

    public void initdata(){

        userrepository.deleteall();

        rolerepository.deleteall();

        departmentrepository.deleteall();

        department department = new department();

        department.setname("開發部");

        departmentrepository.save(department);

        assert.notnull(department.getid());

        role role = new role();

        role.setname("admin");

        rolerepository.save(role);

        assert.notnull(role.getid());

        user user = new user();

        user.setname("user");

        user.setcreatedate(new date());

        user.setdeparment(department);

        list<role> roles = rolerepository.findall();

        assert.notnull(roles);

        user.setroles(roles);

        userrepository.save(user);

        assert.notnull(user.getid());

    @test

    public void findpage(){

        pageable pageable = new pagerequest(0, 10, new sort(sort.direction.asc, 

"id"));

        page<user> page = userrepository.findall(pageable);

        assert.notnull(page);

        for(user user : page.getcontent()) {

            logger.info("====user==== user name:{}, department name:{}, role 

name:{}",

                user.getname(), user.getdeparment().getname(), user.getroles().

get(0).getname());

        }

好了,現在可以使用junit來運作這個測試程式了,在idea的run/debug conf?iguration配置中增加一個juint配置項,子產品選擇mysql,工作目錄選擇子產品所在的根目錄,程式選擇dbdemo.mysql.test.mysqltest,并将配置項目名稱儲存為mysqltest,如圖2-3所示。

用debug方式運作測試配置項目mysqltest,可以在控制台中看到執行的過程和結果。如果狀态欄中顯示為綠色,并且提示“all tests passed”,則表示測試全部通過。在控制台中也可以查到下列列印資訊:

dbdemo.mysql.test.mysqltest - ====user==== user name:user, department name:開發部, role name:admin

這時如果在mysql伺服器中檢視資料庫test,不但可以看到表結構都已經建立了,還可以看到上面測試生成的一些資料。

這是不是很激動人心?在spring boot使用資料庫,就是可以如此簡單和有趣。到目前為止,我們不僅沒有寫過一條查詢語句,也沒有實作一個通路資料庫的方法,但是已經能對資料庫執行所有的操作,包括一般的增删查改和分頁查詢。

圖2-3 juint測試配置

2.2 使用redis

關系型資料庫在性能上總是存在一些這樣那樣的缺陷,是以大家有時候在使用傳統關系型資料庫時,會與具有高效存取功能的緩存系統結合使用,以提高系統的通路性能。在很多流行的緩存系統中,redis是一個不錯的選擇。redis是一種可以持久存儲的緩存系統,是一個高性能的key-value資料庫,它使用鍵-值對的方式來存儲資料。

2.2.1 redis依賴配置

需要使用redis,可在工程的maven配置中加入spring-boot-starter-redis依賴,如代碼清單2-9所示。其中gson是用來轉換json資料格式的工具,mysql是引用了上一節的子產品,這裡使用2.1節定義的實體對象來存取資料,示範在redis中的存取操作。

代碼清單2-9 redis子產品的maven依賴配置

        <artifactid>spring-boot-starter-redis</artifactid>

        <groupid>com.google.code.gson</groupid>

        <artifactid>gson</artifactid>

        <version>2.2.4</version>

        <groupid>springboot.db</groupid>

        <artifactid>mysql</artifactid>

        <version>${project.version}</version>

2.2.2 建立redis服務類

redis提供了下列幾種資料類型可供存取:

string;

hash;

list;

set及zset。

在執行個體中,将使用string即字元串的類型來示範資料的存取操作。對于redis,spring boot沒有提供像jpa那樣相應的資源庫接口,是以隻能仿照上一節中repository的定義編寫一個實體user的服務類,如代碼清單2-10所示。這個服務類可以存取對象user以及由user組成的清單list,同時還提供了一個删除的方法。所有這些方法都是使用redistemplate來實作的。

代碼清單2-10 使用者實體的redis服務類

public class userredis {

    private redistemplate<string, string> redistemplate;

    public void add(string key, long time,user user) {

        gson gson = new gson();

        redistemplate.opsforvalue().set(key, gson.tojson(user), time, timeunit.

minutes);

    public void add(string key, long time, list<user> users) {

        redistemplate.opsforvalue().set(key, gson.tojson(users), time, timeunit.

    public user get(string key) {

        user user = null;

        string userjson = redistemplate.opsforvalue().get(key);

        if(!stringutils.isempty(userjson))

            user = gson.fromjson(userjson, user.class);

        return user;

    public list<user> getlist(string key) {

        list<user> ts = null;

        string listjson = redistemplate.opsforvalue().get(key);

        if(!stringutils.isempty(listjson))

            ts = gson.fromjson(listjson, new typetoken<list<user>>(){}.gettype());

        return ts;

    public void delete(string key){

        redistemplate.opsforvalue().getoperations().delete(key);

redis沒有表結構的概念,是以要實作mysql資料庫中表的資料(即普通java對象映射的實體資料)在redis中存取,必須做一些轉換,使用json格式的文本作為redis與java普通對象互相交換資料的存儲格式。這裡使用gson工具将類對象轉換為json格式的文本進行存儲,要取出資料時,再将json文本資料轉化為java對象。

因為redis使用了key-value的方式存儲資料,是以存入時要生成一個唯一的key,而要查詢或者删除資料時,就可以使用這個唯一的key進行相應的操作。

儲存在redis資料庫中的資料預設是永久存儲的,可以指定一個時限來确定資料的生命周期,超過指定時限的資料将被redis自動清除。在代碼清單2-10中我們以分鐘為機關設定了資料的存儲期限。

另外,為了能正确調用redistemplate,必須對其進行一些初始化工作,即主要對它存取的字元串進行一個json格式的系列化初始配置,如代碼清單2-11所示。

代碼清單2-11 redistemplate初始化

public class redisconfig {

    public redistemplate<string, string> redistemplate(

            redisconnectionfactory factory) {

        stringredistemplate template = new stringredistemplate(factory);

        jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class);

        objectmapper om = new objectmapper();

        om.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any);

        om.enabledefaulttyping(objectmapper.defaulttyping.non_final);

        jackson2jsonredisserializer.setobjectmapper(om);

        template.setvalueserializer(jackson2jsonredisserializer);

        template.afterpropertiesset();

        return template;

2.2.3 redis測試

如果還沒有安裝redis伺服器,可以參照本書附錄c提供的方法安裝,然後在工程的配置檔案application.yml中配置連接配接redis伺服器等參數,如代碼清單2-12所示。其中host和port分别表示redis資料庫伺服器的ip位址和開放端口,database可以不用指定,由redis根據存儲情況自動標明(注:測試時這些配置是內建在一個配置類中實作的)。

代碼清單2-12 redis配置

    redis:

    # database: 1

        host: 192.168.1.214

        port: 6379

        pool:

            max-idle: 8

            min-idle: 0

            max-active: 8

            max-wait: -1

現在編寫一個juint測試程式,來示範如何在redis伺服器中存取資料,如代碼清單2-13所示。測試程式建立一個部門對象并将其命名為“開發部”,建立一個角色對象并把它命名為admin,建立一個使用者對象并把它命名為user,同時設定這個使用者屬于“開發部”,并把admin這個角色配置設定給這個使用者。接着測試程式使用類名等參數生成一個key,并使用這個key清空原來的資料,然後用這個key存儲現在這個使用者的資料,最後使用這個key查詢使用者,并将查到的資訊列印出來。

代碼清單2-13 redis測試程式

@contextconfiguration(classes = {redisconfig.class, userredis.class})

public class redistest {

    private static logger logger = loggerfactory.getlogger(redistest.class);

    userredis userredis;

    public void setup(){

        deparment deparment = new deparment();

        deparment.setname("開發部");

        user.setdeparment(deparment);

        list<role> roles = new arraylist<>();

        roles.add(role);

        userredis.delete(this.getclass().getname()+":userbyname:"+user.getname());

        userredis.add(this.getclass().getname()+":userbyname:"+user.getname(), 10l, user);

    public void get(){

        user user = userredis.get(this.getclass().getname()+":userbyname:

user");

        assert.notnull(user);

        logger.info("======user====== name:{}, deparment:{}, role:{}",

            user.getname(), user.getdeparment().getname(), user.getroles().get(0).

getname());

要運作這個測試程式,可以在idea的run/debug conf?iguration配置中增加一個juint配置項目,子產品選擇redis,工作目錄選擇子產品所在的根目錄,類選擇這個測試程式即dbdemo.redis.test.redistest,并将配置儲存為redistest。

使用debug方式運作測試項目redistest。如果測試通過,會輸出一個使用者的使用者名、所屬部門和擁有角色等簡要資訊,如下所示:

dbdemo.redis.test.redistest - ======user====== name:user, deparment:開發部, role:admin

對于redis的使用,還可以将注解方式與調用資料庫的方法相結合,那樣就不用再編寫像上面那樣的服務類,并且使用起來更加簡單,這将在後面的章節中介紹。

2.3 使用mongodb

在目前流行的nosql資料庫中,mongodb是大家接觸比較早而且用得比較多的資料庫。mongodb是文檔型的nosql資料庫,具有大資料量、高并發等優勢,但缺點是不能建立實體關系,而且也沒有事務管理機制。

2.3.1 mongodb依賴配置

在spring boot中使用mongodb也像使用jpa一樣容易,并且同樣擁有功能完善的資源庫。同樣的,要使用mongodb,首先必須在工程的maven中引入它的依賴,如代碼清單2-14所示。除了mongodb本身的依賴之外,還需要一些附加的工具配套使用。

代碼清單2-14 使用mongodb的maven依賴配置

        <groupid>org.springframework.data</groupid>

        <artifactid>spring-data-mongodb</artifactid>

        <groupid>org.pegdown</groupid>

        <artifactid>pegdown</artifactid>

        <version>1.4.1</version>

        <artifactid>spring-boot-starter-hateoas</artifactid>

        <groupid>com.fasterxml.jackson.core</groupid>

        <artifactid>jackson-annotations</artifactid>

2.3.2 文檔模組化

mongodb是文檔型資料庫,使用mongodb也可以像使用關系型資料庫那樣為文檔模組化。如代碼清單2-15所示,為使用者文檔模組化,它具有使用者名、密碼、使用者名稱、郵箱和注冊日期等字段,有一個用來儲存使用者角色的資料集,還定義了一個構造函數,可以很友善地用來建立一個使用者執行個體。

代碼清單2-15 使用者文檔模組化

@document(collection = "user")

public class user {

    private string userid;

    @notnull @indexed(unique = true)

    private string username;

    @notnull

    private string password;

    private string email;

    private date registrationdate = new date();

    private set<string> roles = new hashset<>();

    public user() { }

    @persistenceconstructor

    public user(string userid, string username, string password, string name, string email,

            date registrationdate, set<string> roles) {

        this.userid = userid;

        this.username = username;

        this.password = password;

        this.name = name;

        this.email = email;

        this.registrationdate = registrationdate;

        this.roles = roles;

2.3.3 文檔持久化

mongodb也有像使用jpa那樣的資源庫,如代碼清單2-16所示,為使用者文檔建立了一個repository接口,繼承于mongorepository,實作了文檔持久化。

代碼清單2-16 使用者文檔持久化

public interface userrepository extends mongorepository<user, string> {

    user findbyusername(string username);

mongorepository的繼承關系如圖2-4所示,看起來跟jpa的資源庫的繼承關系沒有什麼兩樣,它也包含通路資料庫的豐富功能。

代碼清單2-17是用在測試中的使用mongodb的一個配置類定義,其中@propertysource指定讀取資料庫配置檔案的位置和名稱,@enablemongorepositories啟用資源庫并設定定義資源庫接口放置的位置,這裡使用環境變量environment來讀取配置檔案的一些資料庫配置參數,然後使用一個資料庫用戶端,連接配接mongodb伺服器。

代碼清單2-17 testdatasourceconfig配置類

@enablemongorepositories(basepackages = "dbdemo.mongo.repositories")

@propertysource("classpath:test.properties")

public class testdatasourceconfig extends abstractmongoconfiguration {

    @autowired private environment env;

    @override

    public string getdatabasename(){

        return env.getrequiredproperty("mongo.name");

    public mongo mongo() throws exception {

        serveraddress serveraddress = new serveraddress(env.getrequiredproperty

("mongo.host"));

        list<mongocredential> credentials = new arraylist<>();

        return new mongoclient(serveraddress, credentials);

2.3.4 mongodb測試

如果還沒有安裝mongodb伺服器,可以參照附錄b的方法安裝并啟動一個mongodb伺服器。然後,使用如代碼清單2-18所示的配置方法配置連接配接伺服器的一些參數,該配置假定你的mongodb伺服器安裝在本地,并使用預設的資料庫端口:27017。

代碼清單2-18 mongodb資料庫配置

# mongodb

mongo.host=localhost

mongo.name=test

mongo.port=27017

這樣就可以編寫一個juint測試例子來測試userrepository接口的使用情況,如代碼清單2-19所示。測試例子首先使用使用者文檔類建立一個使用者對象執行個體,然後使用資源庫接口調用save方法将使用者對象儲存到資料庫中,最後使用f?indall方法查詢所有使用者的清單,并使用一個循環輸出使用者的簡要資訊。

代碼清單2-19 mongodb測試

@contextconfiguration(classes = {testdatasourceconfig.class})

@fixmethodorder

public class repositorytests {

    private static logger logger = loggerfactory.getlogger(repositorytests.class);

    @suppresswarnings("springjavaautowiringinspection") @autowired

        set<string> roles = new hashset<>();

        roles.add("manage");

        user user = new user("1","user","12345678","name","[email protected]",new date(), 

roles);

    public void findall(){

        list<user> users = userrepository.findall();

        assert.notnull(users);

        for(user user : users){

            logger.info("===user=== userid:{}, username:{}, pass:{}, registra

tiondate:{}",

                user.getuserid(), user.getname(), user.getpassword(), user.

getregistrationdate());

現在可以在idea的run/debug conf?iguration配置中增加一個juint測試項目,子產品選擇mongodb,工作目錄選擇子產品所在的工程根目錄,類選擇上面編寫的測試例子,即dbdemo.mongo.test.repositorytests,并将配置儲存為mongotest。

使用debug方式運作測試項目mongotest。如果通過測試,将輸出查到的使用者的簡要資訊,如下所示:

dbdemo.mongo.test.repositorytests - ===user=== userid:1, username:name, pass:12345678, registrationdate:tue jun 07 14:26:02 cst 2016

這時使用mongodb資料庫用戶端輸入下面的查詢指令,也可以查到這條文檔的詳細資訊,這是一條json結構的文本資訊。

> db.user.find()

{ "_id" : "1", "_class" : "dbdemo.mongo.models.user", "username" : "user", "password" : "12345678", "name" : "name", "email" : "[email protected]", "registrationdate" : isodate("2016-04-13t06:27:02.423z"), "roles" : [ "manage" ] }

2.4 使用neo4j

有沒有既具有傳統關系型資料庫的優點,又具備nosql資料庫優勢的一種資料庫呢?neo4j就是一種這樣的資料庫。neo4j是一個高性能的nosql圖資料庫,并且具備完全事務特性。neo4j将結構化資料存儲在一張圖上,圖中每一個節點的屬性表示資料的内容,每一條有向邊表示資料的關系。neo4j沒有表結構的概念,它的資料用節點的屬性來表示。

2.4.1 neo4j依賴配置

在spring boot中使用neo4j非常容易,因為有spring-data-neo4j提供了強大的支援。首先,在工程的maven管理中引入neo4j的相關依賴,如代碼清單2-20所示。

代碼清單2-20 使用neo4j的maven依賴配置

        <artifactid>spring-boot-starter-data-rest</artifactid>

        <artifactid>spring-data-neo4j</artifactid>

        <version>4.0.0.release</version>

        <groupid>com.voodoodyne.jackson.jsog</groupid>

        <artifactid>jackson-jsog</artifactid>

        <version>1.1</version>

        <scope>compile</scope>

2.4.2 節點和關系實體模組化

雖然neo4j沒有表結構的概念,但它有節點和關系的概念。例如,現在有演員和電影兩個實體,它們的關系表現為一個演員在一部電影中扮演一個角色。那麼就可以建立演員和電影兩個節點實體,和一個角色關系實體。它們的實體-關系模型如圖2-5所示。這個實體-關系模型的定義比起關系型資料庫的實體-關系模型的定義要簡單得多,但是它更加形象和貼切地表現了實體之間的關系。更難能可貴的是,這個實體-關系模型是可以不經過任何轉換而直接存入資料庫的,也就是說,在neo4j圖資料庫中儲存的資料與圖2-5所示的相同,它仍然是一張圖。這對于業務人員和資料庫設計人員來說,它的意義相同。是以使用neo4j資料庫,将在很大程度上減輕了設計工作和溝通成本。

像jpa使用了orm一樣,neo4j使用了對象-圖形映射(object-graph mapping,ogm)的方式來模組化。代碼清單2-21是演員節點實體模組化,使用注解@jsonidentityinfo是防止查詢資料時引發遞歸通路效應,注解@nodeentity标志這個類是一個節點實體,注解@graphid定義了節點的一個唯一性辨別,它将在建立節點時由系統自動生成,是以它是不可缺少的。這個節點預定義了其他兩個屬性,name和born。節點的屬性可以随需要增加或減少,這并不影響節點的使用。

代碼清單2-21 演員節點實體模組化

@jsonidentityinfo(generator=jsoggenerator.class)

@nodeentity

public class actor {

    @graphid long id;

    private int born;

    public actor() { }

代碼清單2-22是電影節點實體模組化,注解@relationship表示list<role>是一個關系清單,其中type設定了關系的類型,direction設定這個關系的方向,relationship.incoming表示以這個節點為終點。addrole定義了增加一個關系的方法。

代碼清單2-22 電影節點實體模組化

public class movie {

    string title;

    string year;

    string tagline;

    @relationship(type="acts_in", direction = relationship.incoming)

    list<role> roles = new arraylist<>();

    public role addrole(actor actor, string name){

        role role = new role(actor,this,name);

        this.roles.add(role);

        return role;

    public movie() { }

代碼清單2-23是角色的關系實體模組化,注解@relationshipentity表明這個類是一個關系實體,并用type指定了關系的類型,其中@startnode指定起始節點的實體,

@endnode指定終止節點的實體,這說明了圖中一條有向邊的起點和終點的定義。其中定義了一個建立關系的構造函數role(actor actor,movie movie,string name),這裡的name參數用來指定這個關系的屬性。

代碼清單2-23 角色關系實體模組化

@relationshipentity(type = "acts_in")

public class role {

    @graphid

    long id;

    string role;

    @startnode

    actor actor;

    @endnode

    movie movie;

    public role(actor actor, movie movie, string name) {

        this.actor = actor;

        this.movie = movie;

        this.role = name;

2.4.3 節點實體持久化

像對其他資料庫的通路和存取等操作一樣,spring-data-neo4j提供了功能豐富的資源庫可供調用,是以,對于演員和電影節點實體,可以建立它們對應的資源庫接口,實作實體的持久化。代碼清單2-24是電影資源庫接口的定義,它繼承于graphrepository接口,實作了電影實體的持久化。使用相同方法可以對演員的節點實體實作持久化。關系實體卻不用實作持久化,當儲存節點實體時,節點實體的關系将會同時儲存。

代碼清單2-24 電影實體持久化

public interface movierepository extends graphrepository<movie> {

    movie findbytitle(@param("title") string title);

其中graphrepository接口的繼承關系也遵循了spring boot資源庫定義的規則,即使用與jpa相同的标準規範,是以它同樣包含使用資料庫的豐富功能,如圖2-6所示。

2.4.4 neo4j測試

代碼清單2-24是neo4j的資料庫配置類,其中@enable-transactionmanagement啟用了事務管理,@enableneo4jre-positories啟用了neo4j資源庫并指定了我們定義的資源庫接口的位置,在重載的sessionfactory函數中設定了定義實體的位置,這将促使定義的實體被作為域對象導入,remoteserver設定連接配接neo4j伺服器的url、使用者名和密碼,這些參數要依據安裝neo4j伺服器的情況來設定。如果還沒有安裝neo4j伺服器,可參考附錄a的方法進行安裝,安裝完成後啟動伺服器以備使用。

代碼清單2-25 neo4j配置類

@enabletransactionmanagement

@enableneo4jrepositories(basepackages = { "dbdemo.neo4j.repositories" })

public class neo4jconfig extends neo4jconfiguration {

    public neo4jserver neo4jserver() {

        return new remoteserver("http://192.168.1.221:7474","neo4j","12345678");

    public sessionfactory getsessionfactory() {

        return new sessionfactory("dbdemo.neo4j.domain");

現在可以編寫一個測試程式來驗證和示範上面編寫的代碼的功能,如代碼清

單2-26所示。這個測試程式分别建立了三部電影和三個演員,以及三個演員在三部電影中各自扮演的角色,然後按照電影标題查出一部電影,按照其内在的關系輸出這部電影的資訊和每個演員扮演的角色。這些資料的内容參照了neo4j幫助文檔中提供的示例資料。

代碼清單2-26 使用neo4j的juint測試程式

@contextconfiguration(classes = {neo4jconfig.class})

public class movietest {

    private static logger logger = loggerfactory.getlogger(movietest.class);

    movierepository movierepository;

        movierepository.deleteall();

        movie matrix1 = new movie();

        matrix1.settitle("the matrix");

        matrix1.setyear("1999-03-31");

        movie matrix2 = new movie();

        matrix2.settitle("the matrix reloaded");

        matrix2.setyear("2003-05-07");

        movie matrix3 = new movie();

        matrix3.settitle("the matrix revolutions");

        matrix3.setyear("2003-10-27");

        actor keanu = new actor();

        keanu.setname("keanu reeves");

        actor laurence = new actor();

        laurence.setname("laurence fishburne");

        actor carrieanne = new actor();

        carrieanne.setname("carrie-anne moss");

        matrix1.addrole(keanu,  "neo");

        matrix1.addrole(laurence, "morpheus");

        matrix1.addrole(carrieanne,  "trinity");

        movierepository.save(matrix1);

        assert.notnull(matrix1.getid());

        matrix2.addrole(keanu, "neo");

        matrix2.addrole(laurence, "morpheus");

        matrix2.addrole(carrieanne,  "trinity");

        movierepository.save(matrix2);

        assert.notnull(matrix2.getid());

        matrix3.addrole(keanu, "neo");

        matrix3.addrole(laurence, "morpheus");

        matrix3.addrole(carrieanne, "trinity");

        movierepository.save(matrix3);

        assert.notnull(matrix3.getid());

        movie movie = movierepository.findbytitle("the matrix");

        assert.notnull(movie);

        logger.info("===movie=== movie:{}, {}",movie.gettitle(), movie.getyear());

        for(role role : movie.getroles()){

            logger.info("====== actor:{}, role:{}", role.getactor().getname(), role.getrole());

在idea的run/debug conf?iguration配置中增加一個juint的配置項目,子產品選擇neo4j,工作目錄選擇子產品所在的根目錄,測試程式選擇movietest這個類,并将配置儲存為neo4jtest。

使用debug模式運作測試項目neo4jtest,如果測試通過,将在控制台中看到輸出查詢的這部電影和所有演員及其扮演的角色,如下所示:

=== movie=== movie:the matrix, 1999-03-31

====== actor:keanu reeves, role:neo

====== actor:laurence fishburne, role:morpheus

====== actor:carrie-anne moss, role:trinity

這時,在資料庫用戶端的控制台上,單擊左面側邊欄的關系類型acts_in,可以看到一個很酷的圖形,圖中每部電影和每個演員是一個節點,節點的每條有向邊代表了這個演員在那部電影中扮演的角色,如圖2-7所示。

圖2-7 演員和電影的角色關系圖

2.5 小結

這一章,我們一口氣學習使用了4種資料庫:mysql、redis、mongodb、neo4j,除了redis以外,都使用了由spring boot提供的資源庫來通路資料庫并對資料庫執行了一般的存取操作。可以看出,在spring boot架構中使用資料庫非常簡單、容易,這主要得益于spring boot資源庫的強大功能,spring boot資源庫整合了第三方資源,它把複雜的操作變成簡單的調用,它把所有“辛苦、繁重的事情”都包攬了,然後将“微笑和鮮花”獻給了我們。為此,我們應該說聲謝謝,謝謝開發spring boot架構及所有第三方提供者的程式員們,因為有了他們辛勤的付出,才有了我們今天使用上的便利。

本章執行個體的完整代碼可以在idea中直接從github中檢出:https://github.com/chenfromsz/spring-boot-db.git。

本章執行個體都是使用juint的方式來驗證的,為了能使用友好的界面來運作應用,下一章将介紹如何使用thymeleaf來進行界面設計。

第3章

spring boot界面設計

用spring boot架構設計web顯示界面,我們還是使用mvc(model view controller,模型-視圖-控制器)的概念,将資料管理、事件控制和界面顯示進行分層處理,實作多層結構設計。界面設計,即視圖的設計,主要是組織和處理顯示的内容,界面上的事件響應最終交給了控制器進行處理,由控制器決定是否調用模型進行資料的存取操作,然後再将結果傳回給合适的視圖顯示。

本章的執行個體工程使用分子產品管理,如表3-1所示,即将資料管理獨立成為一個工程子產品,專門負責資料庫管理方面的功能。而界面設計子產品主要負責控制器和視圖設計方面的功能。

表3-1 執行個體工程子產品清單

資料管理子產品 data 實作使用neo4j資料庫

界面設計子產品 webui 控制器和視圖設計

3.1 模型設計

資料管理子產品實作了mvc中模型的設計,主要負責實體模組化和資料庫持久化等方面的功能。在本章的執行個體中,将使用上一章的neo4j資料庫的例子,對電影資料進行管理。回顧一下,有兩個節點實體(電影和演員)和一個關系實體(角色)。其中,關系實體展現了節點實體之間的關系,即一個演員在一部電影中扮演一個角色。實體模組化和持久化與上一章的實作差不多。隻不過為了适應本章的内容,電影節點實體和角色關系實體的模組化在屬性上做了些許調整。另外針對neo4j資料庫的分頁查詢也做了一些調整和優化。

3.1.1 節點實體模組化

如代碼清單3-1所示,在電影節點實體模組化中做了一些調整,即增加一個photo屬性,用來存放電影劇照,并将關系類型更改為“扮演”。需要注意的是,neo4j還沒有日期格式的資料類型,是以在讀取日期類型的資料時,使用注解@datetimeformat進行格式轉換,而在儲存時,使用注解@datelong将它轉換成long類型的資料進行存儲。

代碼清單3-1 電影節點實體模組化

    private string photo;

    @datelong

    @relationship(type="扮演", direction = relationship.incoming)

    ......

3.1.2 關系實體模組化

電影實體對應的角色關系實體模組化的關系類型也同樣做了調整而改為“扮演”,如代碼清單3-2所示。

代碼清單3-2 角色關系實體模組化

@relationshipentity(type = "扮演")

    string name;

3.1.3 分頁查詢設計

對于新型的neo4j資料庫來說,由于它的資源庫遵循了jpa的規範标準來設計,在分頁查詢方面有的地方還不是很完善,是以在分頁查詢中,設計了一個服務類來處理,如代碼清單3-3所示。其中,使用class<t>傳入調用的實體對象,使用pageable傳入頁數設定和排序字段設定的參數,使用filters傳入查詢的一些節點屬性設定的參數。

代碼清單3-3 neo4j分頁查詢服務類

@service

public class pagesservice<t> {

    private session session;

    public page<t> findall(class<t> clazz, pageable pageable, filters filters){

        collection data = this.session.loadall(clazz, filters, convert

(pageable.getsort()), new pagination(pageable.getpagenumber(), pageable.getpagesize()), 1);

        return updatepage(pageable, new arraylist(data));

3.2 控制器設計

怎樣将視圖上的操作與模型——資料管理子產品聯系起來,這中間始終是控制器在起着通信橋梁的作用,它響應視圖上的操作事件,然後根據需要決定是否通路資料管理子產品,最後再将結果傳回給合适的視圖,由視圖處理顯示。下面将按照電影控制器的設計來說明控制器中增删查改的實作方法,演員控制器的設計與此類似,不再贅述。

3.2.1 建立控制器

接收建立電影的請求,以及輸入一部電影的資料後的最後送出,由建立控制器進行處理。在控制器上将執行兩個操作,第一個操作将傳回一個建立電影的視圖,第二個操作接收界面中的輸入資料,并調用資料管理子產品進行儲存,如代碼清單3-4所示。其中,create函數将傳回一個建立電影的視圖,它不調用資料管理子產品,save函數将需要儲存的資料通過調用資料管理子產品存儲至資料庫中,并傳回一個成功标志。注意,為了簡化設計,将電影劇照的圖檔檔案做了預定義處理。

代碼清單3-4 建立電影控制器

@requestmapping("/new")

    public modelandview create(modelmap model){

        string[] files = {"/images/movie/西遊記.jpg","/images/movie/西遊記續集.jpg"};

        model.addattribute("files",files);

        return new modelandview("movie/new");

    @requestmapping(value="/save", method = requestmethod.post)

    public string save(movie movie) throws exception{

        movierepository.save(movie);

        logger.info("新增->id={}", movie.getid());

        return "1";

3.2.2 檢視控制器

檢視一個電影的詳細資訊時,控制器首先使用請求的電影id向資料管理子產品請求資料,然後将取得的資料輸出到一個顯示視圖上,如代碼清單3-5所示。

代碼清單3-5 檢視電影控制器

@requestmapping(value="/{id}")

    public modelandview show(modelmap model, @pathvariable long id) {

        movie movie = movierepository.findone(id);

        model.addattribute("movie",movie);

        return new modelandview("movie/show");

3.2.3 修改控制器

若要實作對電影的修改及儲存操作,需要先将電影的資料展示在視圖界面上,然後接收界面的操作,調用資料管理子產品将更改的資料儲存至資料庫中,如代碼清單3-6所示。其中,為了簡化設計,将劇照中的圖檔檔案和電影角色名稱做了預定義處理。修改資料時,由于從界面傳回的電影對象中,丢失了其角色關系的資料(這是ogm的缺點),是以再次查詢一次資料庫,以取得一個電影的完整資料,然後再執行修改的操作。

代碼清單3-6 修改電影控制器

@requestmapping(value="/edit/{id}")

    public modelandview update(modelmap model, @pathvariable long id){

        string[] rolelist = {"唐僧","孫悟空","豬八戒","沙僧"};

        iterable<actor> actors = actorrepository.findall();

        model.addattribute("rolelist",rolelist);

        model.addattribute("actors",actors);

        return new modelandview("movie/edit");

    @requestmapping(method = requestmethod.post, value="/update")

    public string update(movie movie, httpservletrequest request) throws exception{

        string rolename = request.getparameter("rolename");

        string actorid = request.getparameter("actorid");

        movie old = movierepository.findone(movie.getid());

        old.setname(movie.getname());

        old.setphoto(movie.getphoto());

        old.setcreatedate(movie.getcreatedate());

        if(!stringutils.isempty(rolename) && !stringutils.isempty(actorid)) {

            actor actor = actorrepository.findone(new long(actorid));

            old.addrole(actor, rolename);

        movierepository.save(old);

        logger.info("修改->id="+old.getid());

3.2.4 删除控制器

删除電影時,從界面上接收電影的id參數,然後調用資料管理子產品将電影删除,如代碼清單3-7所示。

代碼清單3-7 删除電影控制器

@requestmapping(value="/delete/{id}",method = requestmethod.get)

    public string delete(@pathvariable long id) throws exception{

        movierepository.delete(movie);

        logger.info("删除->id="+id);

3.2.5 分頁查詢控制器

清單資料的查詢使用分頁的方法,按提供的查詢字段參數、頁碼、頁大小及其排序字段等參數,通過調用資料管理子產品進行查詢,然後傳回一個分頁對象page,如代碼清單3-8所示。這裡的分頁查詢調用了3.1.3節定義的分頁查詢服務類。

代碼清單3-8 電影分頁查詢控制器

    @requestmapping(value="/list")

public page<movie> list(httpservletrequest request) throws exception{

    string name = request.getparameter("name");

    string page = request.getparameter("page");

    string size = request.getparameter("size");

    pageable pageable = new pagerequest(page==null? 0: integer.parseint(page), 

size==null? 10:integer.parseint(size),

            new sort(sort.direction.desc, "id"));

    filters filters = new filters();

    if (!stringutils.isempty(name)) {

        filter filter = new filter("name", name);

        filters.add(filter);

    return pagesservice.findall(movie.class, pageable, filters);

3.3 使用thymeleaf模闆

完成了模型和控制器的設計之後,接下來的工作就是視圖設計了。在視圖設計中主要使用thymeleaf模闆來實作。在進行視圖設計之前,先了解一下thymeleaf模闆的功能。

thymeleaf是一個優秀的面向java的xml/xhtml/html 5頁面模闆,并具有豐富的标簽語言和函數。使用spring boot架構進行界面設計,一般都會選擇thymeleaf模闆。

3.3.1 thymeleaf配置

要使用thymeleaf模闆,首先,必須在工程的maven管理中引入它的依賴:“spring-boot-starter-thymeleaf”,如代碼清單3-9所示。

代碼清單3-9 thymeleaf依賴配置

<dependency>

    <groupid>org.springframework.boot</groupid>

    <artifactid>spring-boot-starter-thymeleaf</artifactid>

</dependency>

其次,必須配置使用thymeleaf模闆的一些參數。在一般的web項目中都會使用如代碼清單3-10所示的配置,其中,pref?ix指定了html檔案存放在webapp的/web-inf/views/目錄下面,或者也可以指定其他路徑,其他一些參數的設定其實是使用了thymeleaf的預設設定。

在執行個體中,為了更友善将項目釋出成jar檔案,我們将使用thymeleaf自動配置中的預設配置選項,即隻要在資源檔案夾resoueces中增加一個templates目錄即可,這個目錄用來存放html檔案。

代碼清單3-10 thymeleaf配置

    thymeleaf:

        prefix: /web-inf/views/

        suffix: .html

        mode: html5

        encoding: utf-8

        content-type: text/html

        cache: false

如果工程中增加了thymeleaf的依賴,而沒有進行任何配置,或者增加預設目錄,啟動應用時就會報錯。

3.3.2 thymeleaf功能簡介

在html頁面上使用thymeleaf标簽語言,用一個簡單的關鍵字“th”來标注。使用thymeleaf标簽語言的典型例子如下:

<h3 th:text="${actor.name}"></h3>

<img th:src="@{/images/logo.png}"/>

其中,th:text指定了在标簽<h3>中顯示的文本,它的值來自于關鍵字“$”所引用的記憶體變量,th:src設定了标簽<img>的圖檔檔案的連結位址,既可以是絕對路徑,也可以是相對路徑。下面列出了thymeleaf的一些主要标簽和函數。

th:text,顯示文本。

th:utext:和th:text的差別是針對"unescaped text"。

th:attr:設定标簽屬性。

th:if or th:unless:條件判斷語句。

th:switch,th:case:選擇語句。

th:each:循環語句。

#dates:日期函數。

#calendars:月曆函數。

#numbers:數字函數。

#strings:字元串函數。

#objects:對象函數。

#bools:邏輯函數。

#arrays:數組函數。

#lists:清單函數。

本章的執行個體工程将在視圖設計中使用thymeleaf的下列幾個主要功能,而有關thymeleaf的詳細說明和介紹可以通路它的官方網站http://www.thymeleaf.org/,以獲得更多的幫助。

1.?使用功能函數

thymeleaf有一些日期功能函數、字元串函數、數組函數、清單函數等,代碼清單3-11是thymeleaf使用日期函數的一個例子,#dates.format是一個日期格式化的使用執行個體,它将電影的建立日期格式化為中文環境的使用格式“'yyyy-mm-dd hh:mm:ss'”。

代碼清單3-11 thymeleaf使用函數

th:value="${movie.createdate} ? ${#dates.format(movie.createdate,'yyyy-mm-dd hh:mm:ss')} :''"

2.?使用程式設計語句

thymeleaf有條件語句、選擇語句、循環語句等。代碼清單3-12使用each循環語句來顯示一個資料清單,即在下拉清單框中使用循環語句來顯示所有的演員清單。

代碼清單3-12 th:each循環

<select name="actorid" id="actorid">

<option value="">選擇演員</option>

<option th:each="actor:${actors}"

    th:value="${actor.id}"

    th:text="${actor.name}">

</option>

</select>

3.?使用頁面架構模闆

thymeleaf的頁面架構模闆是比較優秀的功能。預先定義一個layout,它具有頁眉、頁腳、提示欄、導航欄和内容顯示等區域,如代碼清單3-13所示。其中,layout:fragment=

" prompt"是一個提示欄,它可以讓引用的視圖替換顯示的内容;fragments/nav :: nav是一個導航欄并指定了視圖檔案,也就是說它不能被引用的視圖替換内容;layout:fragment="content"是一個主要内容顯示區域,它也能由引用的視圖替換顯示内容;fragments/footer :: footer是一個頁腳定義并且也指定了視圖檔案,即不被引用的視圖替換顯示内容。這樣設計出來的頁面模闆架構如圖3-1所示。

代碼清單3-13 layout模闆

<div class="headerbox">

    <div class="topbox">

        <div class="toplogo f-left">

            <a href="#"><img th:src="@{/images/logo.png}"/></a>

        </div>

        <div class="new-nav">

            <h3>電影頻道</h3>

    </div>

</div>

<div class="locationline" layout:fragment=" prompt ">

    目前位置:首頁 > <em>頁面</em>

<table class="globalmainbox" style="position:relative;z-index:1">

    <tr>

        <td class="columnleftbox" valign="top">

            <div th:replace="fragments/nav :: nav"></div>

        </td>

        <td class="whitespace"></td>

        <td class="rightcolumnbox" valign="top">

            <div layout:fragment="content"></div>

    </tr>

</table>

<div class="footbox" th:replace="fragments/footer :: footer"></div>

圖3-1 頁面架構模闆

有了頁面模闆之後,就可以在一個首頁面視圖上引用上面的layout,并替換它的提示欄prompt和主要内容顯示區域content,其他頁眉、頁腳和導航欄卻保持同樣的内容,如代碼清單3-14所示。這樣就可以設計出一個使用共用模闆的具有統一風格特征的界面。

代碼清單3-14 使用layout模闆的視圖設計

<html xmlns:th="http://www.thymeleaf.org" layout:decorator="fragments/layout">

目前位置:首頁 > <em >電影管理</em>

<div class="statisticbox w-782"  layout:fragment="content">

3.4 視圖設計

視圖設計包括清單視圖、建立視圖、檢視視圖、修改視圖和删除視圖設計等5個方面有關資料的增删查改的内容。

我們知道,視圖上的資料存取不是直接與模型打交道,而是通過控制器來處理。在視圖中對于控制器的請求,大多使用jquery的方式來實作。jquery是一個優秀的javascript程式庫,并且具有很好的相容性,幾乎相容了現有的所有浏覽器。

下面的視圖設計将以電影的視圖設計為例說明,演員的視圖設計與此類似,不再贅述。

3.4.1 清單視圖設計

電影的清單視圖是電影視圖的首頁,它引用了3.3節使用thymeleaf設計的頁面架構模闆layout.html,在這裡主要實作對資料的分頁查詢請求和清單資料顯示,并提供了一部電影的建立、檢視、修改和删除等超連結。

1.?分頁設計

電影的清單視圖的分頁設計使用了“jquery.pagination.js”分頁插件,編寫如代碼清單3-15所示的腳本,其中getopt定義了分頁工具條的一些基本屬性,pageaction通過“./list”調用控制器取得分頁資料清單,f?illdata函數将清單資料填充到html控件tbodycontent中。

代碼清單3-15 分頁設計的js編碼

// 分頁的參數設定

var getopt = function(){

    var opt = {

        items_per_page: 10, // 每頁記錄數

        num_display_entries: 3, // 中間顯示的頁數,預設為10

        current_page:0, // 目前頁

        num_edge_entries:1, // 頭尾顯示的頁數,預設為0

        link_to:"javascript:void(0)",

        prev_text:"上頁",

        next_text:"下頁",

        load_first_page:true,

        show_total_info:true ,

        show_first_last:true,

        first_text:"首頁",

        last_text:"尾頁",

        hasselect:false,

        callback: pageselectcallback // 回調函數

    return opt;

// 分頁開始

var currentpagedata = null ;

var pageaction = function(){

    $.get('./list?t='+new date().gettime(),{

        name:$("#name").val()

    },function(data){

        currentpagedata = data.content;

        $(".pagination").pagination(data.totalelements, getopt());

    });

var pageselectcallback = function(page_index, jq, size){

    if(currentpagedata!=null){

        filldata(currentpagedata);

        currentpagedata = null;

    }else

        $.get('./list?t='+new date().gettime(),{

            size:size,page:page_index,name:$("#name").val()

        },function(data){

            filldata(data.content);

        });

// 填充分頁資料

function filldata(data){

    var $list = $('#tbodycontent').empty();

    $.each(data,function(k,v){

        var html = "" ;

        html += '<tr> ' +

                '<td>'+ (v.id==null?'':v.id) +'</td>' +

                '<td>'+ (v.name==null?'':v.name) +'</td>' +

                '<td>'+ (v.createdate==null?'': getsmpformatdatebylong(v.create

date,true)) +'</td>' ;

        html += '<td><a class="c-50a73f mlr-6" href="javascript:void(0)" onclick=

"detail(\''+ v.id+'\')">檢視</a><a class="c-50a73f mlr-6" href=

"javascript:void(0)" onclick="edit(\''+ v.id+'\')">修改</a><a class="c-50a73f mlr-6" href="javascript:void(0)" onclick="del(\''+ v.id+'\')">删除</a></td>' ;

        html +='</tr>' ;

        $list.append($(html));

// 分頁結束

2.?清單頁面設計

電影清單的顯示頁面主要定義了清單字段的名稱和提供顯示資料内容的控件id,即tbodycontent,如代碼清單3-16所示。

代碼清單3-16 電影清單頁面html編碼

<!doctype html>

<head>

    <title>電影管理</title>

    <link th:href="@{/scripts/pagination/pagination.css}" rel="stylesheet" type="text/css" />

    <link th:href="@{/scripts/artdialog/default.css}" rel="stylesheet" type="text/css" />

    <link th:href="@{/scripts/my97datepicker/skin/wdatepicker.css}" rel="stylesheet" type="text/css" />

    <link th:href="@{/styles/index.css}" rel="stylesheet"  type="text/css"/>

    <script th:src="@{/scripts/pagination/jquery.pagination.js}"></script>

    <script th:src="@{/scripts/jquery.smartselect-1.1.min.js}"></script>

    <script th:src="@{/scripts/my97datepicker/wdatepicker.js}"></script>

    <script th:src="@{/scripts/movie/list.js}"></script>

</head>

<body>

<div class="locationline" layout:fragment="prompt">

    目前位置:首頁 > <em >電影管理</em>

    <form id="queryform" method="get">

    <div class="radiusgraybox782">

        <div class="radiusgraytop782"></div>

        <div class="radiusgraymid782">

            <div class="datasearchbox foruserradius">

                <ul>

                    <li>

                        <label class="preinptxt f-left">電影名稱</label>

                        <input type="text" class="inp-list f-left w-200" place

holder="按電影名稱搜尋" id="name"  name="name"/>

                    </li>

                        <a href="javascript:void(0)" class="bluebtn-62x30 f-right" 

id="searchbtn">查詢</a>

                </ul>

            </div>

    </form>

    <div class="newbtnbox">

        <input type="hidden" id="m_ck" />

        <a id="addbtn" class="bluebtn-62x30" href="javascript:void(0)">新增</a>

    <div class="datadetaillist mt-12">

        <table id="results" class="datalisttab">

            <thead>

            <tr>

                <th>id</th>

                <th>電影</th>

                <th>出版日期</th>

                <th>操作</th>

            </tr>

            </thead>

            <tbody id="tbodycontent">

            </tbody>

        </table>

        <div class="tablefootline">

            <div class="pagebarlist pagination"/>

</body>

</html>

3.?清單視圖設計效果

電影資料清單視圖設計的最終顯示效果如圖3-2所示。

圖3-2 電影清單視圖設計效果圖

3.4.2 建立視圖設計

1.?建立對話框設計

建立電影時,在電影首頁中打開一個對話框顯示建立的操作界面,對話框設計引用了“artdialog.js”的對話框插件,然後編寫一個腳本來打開對話框,如代碼清單3-17所示。其中“./new”是連接配接控制器的請求url,注意這裡使用了相對路徑,這個url通過“$.get”請求傳回建立電影的html頁面,請求連結中的ts參數傳遞的是目前時間,這是為了保證該連結是一個全新的連結,以使浏覽器能顯示一個最新的内容頁面。

代碼清單3-17 建立電影對話框設計js編碼

function create(){

    $.get("./new",{ts:new date().gettime()},function(data){

        art.dialog({

            lock:true,

            opacity:0.3,

            title: "新增",

            width:'750px',

            height: 'auto',

            left: '50%',

            top: '50%',

            content:data,

            esc: true,

            init: function(){

                artdialog = this;

            },

            close: function(){

                artdialog = null;

            }

2.?建立頁面設計

建立電影的頁面設計,如代碼清單3-18所示,這裡隻是部分html編碼,其中的日期控件使用“wdatepicker.js”插件來實作。對于一部電影來說,我們需要輸入名稱、劇照和日期三個屬性,其中劇照的圖檔下拉清單框使用“imageselect.js”圖檔下拉清單框插件來實作,并且為了簡化設計,劇照中的圖檔檔案使用了預先定義的檔案,這裡隻要選擇使用哪一個圖檔即可。

代碼清單3-18 建立電影頁面html編碼

<link th:href="@{/styles/imageselect.css}" rel="stylesheet" type="text/css" />

<script th:src="@{/scripts/imageselect.js}"></script>

<script th:src="@{/scripts/movie/new.js}"></script>

<form id="saveform" action="./save" method="post">

    <table class="addnewinflist">

        <tr>

            <th>名稱</th>

            <td><input class="inp-list w-200 clear-mr f-left" type="text"   id=

"name" name="name"  maxlength="120" /></td>

            <th>劇照</th>

            <td width="240">

                <select name="photo" id="photo">

                    <option th:each="file:${files}"

                            th:value="${file}"

                            th:text="${file}">

                    </option>

                </select>

            </td>

        </tr>

            <th>日期</th>

            <td>

                <input  onfocus="wdatepicker({datefmt:'yyyy-mm-dd hh:mm:ss'})" 

type="text" class="inp-list w-200 clear-mr f-left" id="createdate" name="createdate"/>

    </table>

    <div class="bottombtnbox">

        <a class="btn-93x38 savebtn" href="javascript:void(0)">确定</a>

        <a class="btn-93x38 backbtn" href="javascript: closedialog()">傳回</a>

</form>

<script type="text/javascript">

    $(document).ready(function(){

        $('select[name=photo]').imageselect({dropdownwidth:425});

</script>

3.?表單驗證與送出設計

驗證建立電影表單的送出時使用“jquery.validate.min.js”插件中的驗證方法來實作,如代碼清單3-19所示。儲存時調用經典的“$.ajax”方式利用post方法進行送出,其中headers: {"content-type": "application/x-www-form-urlencoded;charset=utf-8"}用于保證資料在傳輸過程中中文字元的正确性。在表單驗證中,隻對name和createdate兩個屬性進行簡單的非空驗證,表單的參數傳遞使用一個表單序列化函數serialize()來實作,它将表單控件上的對象序列化為一個個含有“鍵-值”對的字元串進行送出。

代碼清單3-19 建立電影中表單驗證和送出的js編碼

$(function(){

        $('#saveform').validate({

                rules: {

            name       :{required:true},

            createdate      :{required:true}

                },messages:{

            name :{required:"必填"},

            createdate :{required:"必填"}

    $('.savebtn').click(function(){

        if($('#saveform').valid()){

            $.ajax({

                type: "post",

                url: "./save",

                data: $("#saveform").serialize(),

                headers: {"content-type": "application/x-www-form-urlencoded;

charset=utf-8"},

                success: function (data) {

                    if (data == 1) {

                        alert("儲存成功");

                        pageaction();

                        closedialog();

                    } else {

                        alert(data);

                    }

                },

                error:function(data){

                    var e;

                    $.each(data,function(v){

                        e += v + " ";

                    });

                    alert(e);

                }

            });

        }else{

            alert('資料驗證失敗,請檢查!');

});

4.?建立視圖設計效果

建立電影的視圖設計最後的顯示效果如圖3-3所示。

圖3-3 建立電影視圖設計效果圖

3.4.3 檢視視圖設計

1.?檢視對話框設計

在電影的首頁中單擊一部電影的檢視連結,将打開一個檢視電影的對話框,對話框的設計如代碼清單3-20所示,其中“./{id}”是提取資料的連結,它将向控制器請求資料,并以html頁面方式顯示出來。

代碼清單3-20 檢視電影對話框js編碼

function detail(id){

    $.get("./"+id,{ts:new date().gettime()},function(data){

            title: "檢視資訊",

2.?檢視頁面設計

電影檢視頁面的設計,即将資料展示出來的html編碼,如代碼清單3-21所示,需要注意的是,日期資料需要進行格式化,而演員表則使用thymeleaf中的一個“th:each”循環語句來輸出。

代碼清單3-21 電影檢視頁面html編碼

<div class="addinfbtn">

    <h3 class="itemtit"><span>電影資訊</span></h3>

            <td width="240"><input class="inp-list w-200 clear-mr f-left" type=

"text" th:value="${movie.name}" id="name" name="name" maxlength="16" /></td>

                <input  onfocus="wdatepicker({datefmt:'yyyy-mm-dd hh:mm:ss'})" type="text" class="inp-list w-200 clear-mr f-left" th:value="${movie.createdate} ? ${#dates.format(movie.createdate,'yyyy-mm-dd hh:mm:ss')} :''" id="createdate" name="createdate"/>

                <img th:src="${movie.photo}"/>

            <th>演員表</th>

                    <li th:each="role:${movie.roles}" th:text="${role.actor.

name}+' 飾 '+${role.name}"></li>

        <a class="btn-93x38 backbtn" href="javascript:closedialog(0)">傳回</a>

3.?檢視視圖的設計效果

電影檢視視圖設計最終完成的效果如圖3-4所示。

圖3-4 檢視電影視圖設計效果圖

3.4.4 修改視圖設計

1.?修改對話框設計

在電影的首頁中修改一部電影,首先打開一個修改電影的對話框,這個對話框的設計如代碼清單3-22所示。其中通過“$.get”通路“./edit/{id}”取得資料和修改視圖的html頁面元素。

代碼清單3-22 修改電影對話框js編碼

function edit(id){

    $.get("./edit/"+id,{ts:new date().gettime()},function(data){

            title: "修改",

2.?修改頁面設計

修改電影視圖的頁面設計如代碼清單3-23所示,其中劇照的下拉清單框中增加了“選中”的代碼:th:selected="${movie.photo == f?ile}",即如果電影中的劇照與下拉框清單中的劇照相同,則選中它。

在修改界面上,還增加了“增加角色”和“選擇演員”的編輯項。為了簡化設計這裡的角色名稱我們也使用了預先定義的資料。

代碼清單3-23 修改電影頁面html編碼

<script th:src="@{/scripts/movie/edit.js}"></script>

<form id="saveform" method="post">

    <input type="hidden" name="id" id="id" th:value="${movie.id}"/>

<div class="addinfbtn" >

    <h3 class="itemtit"><span>編輯資訊</span></h3>

            <th>電影名稱</th>

            <th>電影劇照</th>

                            th:text="${file}"

                            th:selected="${movie.photo == file}">

            <th>出版日期</th>

type="text" class="inp-list w-200 clear-mr f-left" th:value="${movie.createdate} ? ${#dates.format(movie.createdate,'yyyy-mm-dd hh:mm:ss')} :''" id="createdate" name="createdate"/>

            <th>增加角色</th>

                <select name="rolename" id="rolename">

                    <option value="">增加角色</option>

                    <option th:each="role:${rolelist}"

                            th:value="${role}"

                            th:text="${role}">

            <th>選擇演員</th>

                <select name="actorid" id="actorid">

                    <option value="">選擇演員</option>

                    <option th:each="actor:${actors}"

                            th:value="${actor.id}"

                            th:text="${actor.name}">

3.?修改視圖的設計效果

最終完成的修改電影視圖的顯示效果如圖3-5所示。

圖3-5 修改電影視圖設計效果圖

3.4.5 删除視圖設計

1.?删除确認對話框

如果有删除的操作,首先要給出确認提示框,隻有使用者單擊确定後才能删除資料,否則将不做任何操作。确認提示框是調用了windows中的确認對話框,如代碼清單3-24所示。

代碼清單3-24 删除确認對話框js編碼

function del(id){

    if(!confirm("您确定删除此記錄嗎?")){

        return false;

    $.get("./delete/"+id,{ts:new date().gettime()},function(data){

        if(data==1){

            alert("删除成功");

            pageaction();

            alert(data);

2.?删除确認設計效果

執行删除操作的确認效果如圖3-6所示。

3.5 運作與釋出

本章執行個體工程的完整代碼可以通過idea從github中檢出:https://github.com/chenfromsz/spring-boot-ui.git。spring boot需要一個啟動程式作為應用的入口,在webui子產品中,我們設計了一個入口程式,如代碼清單3-25所示。使用這個入口程式,就可以調試和釋出工程了。

代碼清單3-25 web應用啟動主程式

package com.test.webui;

import org.springframework.context.annotation.componentscan;

@componentscan(basepackages = "com.test")

public class webuiapp {

        springapplication.run(webuiapp.class, args);

通過在idea中打開run/debug conf?igurations對話框,增加一個spring boot配置,子產品選擇webui,工作目錄選擇子產品webui所在的路徑,主程式選擇webuiapp,并将配置儲存為webui。然後在idea中運作該配置項目webui,即可啟動應用進行調試。

如果要釋出應用,可以在idea的run/debug conf?igurations對話框中增加一個maven打包配置項目,工作目錄選擇工程的根目錄,指令行中輸入指令:clean package-d skiptests,并将配置儲存為mvn。然後運作這個配置項目mvn進行打包,打包成功後,在“webui/target”目錄中将生成webui-1.0-snapshot.jar。要運作這個程式包,可以打開一個指令行視窗,将路徑切換到webui-1.0-snapshot.jar所在的目錄,使用下列指令即可運作應用。

java -jar webui-1.0-snapshot.jar

最後可使用下面的url進行通路:

http://localhost

在執行個體中增加了一些資料之後,在neo4j資料庫用戶端中單擊“扮演”關系,也可以看到電影和演員的關系圖,如圖3-7所示。

3.6 小結

本章介紹了使用mvc的多層結構方式,以及在spring boot進行web界面設計的方法,并且使用thymeleaf模闆設計了一個web應用的頁面架構。web界面設計的一些細節,更多的是使用了html編碼和javascript腳本,而html離不開css的支援,javascript更是借助于jquery及其各種插件的功能。讀者如需深入了解這方面的知識和技術,可查找相關的知識進行學習和研究。這裡主要使用thymeleaf模闆工具來設計整體界面以及組織和處理資料的顯示。

有了顯示界面之後,對資料庫的操作就更為友善和直覺了。下一章将介紹如何使用一些技術手段來提升通路資料庫的性能,以及怎樣擴充通路資料庫的功能。