1. 程式人生 > >一步步教你整合SSM框架(Spring MVC+Spring+MyBatis)詳細教程

一步步教你整合SSM框架(Spring MVC+Spring+MyBatis)詳細教程

SSM(Spring+SpringMVC+Mybatis)是目前較為主流的企業級架構方案,不知道大家有沒有留意,在我們看招聘資訊的時候,經常會看到這一點,需要具備SSH框架的技能;而且在大部分教學課堂中,也會把SSH作為最核心的教學內容。

但是,我們在實際應用中發現,SpringMVC可以完全替代Struts,配合註解的方式,程式設計非常快捷,而且通過restful風格定義url,讓地址看起來非常優雅。

另外,MyBatis也可以替換Hibernate,正因為MyBatis的半自動特點,我們程式猿可以完全掌控SQL,這會讓有資料庫經驗的程式猿能開發出高效率的SQL語句,而且XML配置管理起來也非常方便。

好了,如果你也認同我的看法,那麼下面我們一起來做整合吧!話不多說,來一起看看詳細的介紹:

在寫程式碼之前我們先了解一下這三個框架分別是幹什麼的?

相信大以前也看過不少這些概念,我這就用大白話來講,如果之前有了解過可以跳過這一大段,直接看程式碼!

  1. SpringMVC:它用於web層,相當於controller(等價於傳統的servlet和struts的action),用來處理使用者請求。舉個例子,使用者在位址列輸入http://網站域名/login,那麼springmvc就會攔截到這個請求,並且呼叫controller層中相應的方法,(中間可能包含驗證使用者名稱和密碼的業務邏輯,以及查詢資料庫操作,但這些都不是springmvc的職責),最終把結果返回給使用者,並且返回相應的頁面(當然也可以只反饋josn/xml等格式資料)。springmvc就是做前面和後面過程的活,與使用者打交道!!
  2. Spring:太強大了,以至於我無法用一個詞或一句話來概括它。但與我們平時開發接觸最多的估計就是IOC容器,它可以裝載bean(也就是我們java中的類,當然也包括service dao裡面的),有了這個機制,我們就不用在每次使用這個類的時候為它初始化,很少看到關鍵字new。另外spring的aop,事務管理等等都是我們經常用到的。
  3. MyBatis:如果你問我它跟鼎鼎大名的Hibernate有什麼區別?我只想說,他更符合我的需求。第一,它能自由控制sql,這會讓有資料庫經驗的人(當然不是說我啦~捂臉~)編寫的程式碼能搞提升資料庫訪問的效率。第二,它可以使用xml的方式來組織管理我們的sql,因為一般程式出錯很多情況下是sql出錯,別人接手程式碼後能快速找到出錯地方,甚至可以優化原來寫的sql。

SSM框架整合配置

好了,前面bb那麼多,下面我們真正開始敲程式碼了~

首先我們開啟IED,我這裡用的是eclipse(你們應該也是用的這個,對嗎?),建立一個動態web專案,建立好相應的目錄結構(重點!)

(打了馬賽克是因為這裡還用不到,你們不要那麼汙好不好?)

我說一下每個目錄都有什麼用吧(第一次畫表格,我發現markdown的表格語法很不友好呀~)

這個目錄結構同時也遵循maven的目錄規範~

檔名 作用
src 根目錄,沒什麼好說的,下面有main和test。
main 主要目錄,可以放java程式碼和一些資原始檔。
java 存放我們的java程式碼,這個資料夾要使用Build Path -> Use as Source Folder,這樣看包結構會方便很多,新建的包就相當於在這裡新建資料夾咯。
resources 存放資原始檔,譬如各種的spring,mybatis,log配置檔案。
mapper 存放dao中每個方法對應的sql,在這裡配置,無需寫daoImpl。
spring 這裡當然是存放spring相關的配置檔案,有dao service web三層。
sql 其實這個可以沒有,但是為了專案完整性還是加上吧。
webapp 這個貌似是最熟悉的目錄了,用來存放我們前端的靜態資源,如jsp js css。
resources 這裡的資源是指專案的靜態資源,如js css images等。
WEB-INF 很重要的一個目錄,外部瀏覽器無法訪問,只有羨慕內部才能訪問,可以把jsp放在這裡,另外就是web.xml了。你可能有疑問了,為什麼上面java中的resources裡面的配置檔案不妨在這裡,那麼是不是會被外部竊取到?你想太多了,部署時候基本上只有webapp裡的會直接輸出到根目錄,其他都會放入WEB-INF裡面,專案內部依然可以使用classpath:XXX來訪問,好像IDE裡可以設定部署輸出目錄,這裡扯遠了~
test 這裡是測試分支。
java 測試java程式碼,應遵循包名相同的原則,這個資料夾同樣要使用Build Path -> Use as Source Folder,這樣看包結構會方便很多。

resources 沒什麼好說的,好像也很少用到,但這個是maven的規範。

我先新建好幾個必要的包,併為大家講解一下每個包的作用,順便理清一下後臺的思路~

包名 名稱 作用
dao 資料訪問層(介面) 與資料打交道,可以是資料庫操作,也可以是檔案讀寫操作,甚至是redis快取操作,總之與資料操作有關的都放在這裡,也有人叫做dal或者資料持久層都差不多意思。為什麼沒有daoImpl,因為我們用的是mybatis,所以可以直接在配置檔案中實現介面的每個方法。
entity 實體類 一般與資料庫的表相對應,封裝dao層取出來的資料為一個物件,也就是我們常說的pojo,一般只在dao層與service層之間傳輸。
dto 資料傳輸層 剛學框架的人可能不明白這個有什麼用,其實就是用於service層與web層之間傳輸,為什麼不直接用entity(pojo)?其實在實際開發中發現,很多時間一個entity並不能滿足我們的業務需求,可能呈現給使用者的資訊十分之多,這時候就有了dto,也相當於vo,記住一定不要把這個混雜在entity裡面,答應我好嗎?
service 業務邏輯(介面) 寫我們的業務邏輯,也有人叫bll,在設計業務介面時候應該站在“使用者”的角度。額,不要問我為什麼這裡沒顯示!IDE調皮我也拿它沒辦法~
serviceImpl 業務邏輯(實現) 實現我們業務介面,一般事務控制是寫在這裡,沒什麼好說的。
web 控制器 springmvc就是在這裡發揮作用的,一般人叫做controller控制器,相當於struts中的action。

還有最後一步基礎工作,匯入我們相應的jar包,我使用的是maven來管理我們的jar,所以只需要在poom.xml中加入相應的依賴就好了,如果不使用maven的可以自己去官網下載相應的jar,放到專案WEB-INF/lib目錄下。關於maven的學習大家可以看慕課網的視訊教程,這裡就不展開了。我把專案用到的jar都寫在下面,版本都不是最新的,大家有經驗的話可以自己調整版本號。另外,所有jar都會與專案一起打包放到我的github上,喜歡的給個star吧~

poom.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

<modelversion>4.0.0</modelversion>

<groupid>com.soecode.ssm</groupid>

ssm</artifactid>

<packaging>war</packaging>

<version>0.0.1-SNAPSHOT</version>

<name>ssm Maven Webapp</name>

<dependencies>

<!-- 單元測試 -->

<dependency>

<groupid>junit</groupid>

junit</artifactid>

<version>4.11</version>

</dependency>

<!-- 1.日誌 -->

<!-- 實現slf4j介面並整合 -->

<dependency>

<groupid>ch.qos.logback</groupid>

logback-classic</artifactid>

<version>1.1.1</version>

</dependency>

<!-- 2.資料庫 -->

<dependency>

<groupid>mysql</groupid>

mysql-connector-java</artifactid>

<version>5.1.37</version>

<scope>runtime</scope>

</dependency>

<dependency>

<groupid>c3p0</groupid>

c3p0</artifactid>

<version>0.9.1.2</version>

</dependency>

<!-- DAO: MyBatis -->

<dependency>

<groupid>org.mybatis</groupid>

mybatis</artifactid>

<version>3.3.0</version>

</dependency>

<dependency>

<groupid>org.mybatis</groupid>

mybatis-spring</artifactid>

<version>1.2.3</version>

</dependency>

<!-- 3.Servlet web -->

<dependency>

<groupid>taglibs</groupid>

standard</artifactid>

<version>1.1.2</version>

</dependency>

<dependency>

<groupid>jstl</groupid>

jstl</artifactid>

<version>1.2</version>

</dependency>

<dependency>

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

jackson-databind</artifactid>

<version>2.5.4</version>

</dependency>

<dependency>

<groupid>javax.servlet</groupid>

javax.servlet-api</artifactid>

<version>3.1.0</version>

</dependency>

<!-- 4.Spring -->

<!-- 1)Spring核心 -->

<dependency>

<groupid>org.springframework</groupid>

spring-core</artifactid>

<version>4.1.7.RELEASE</version>

</dependency>

<dependency>

<groupid>org.springframework</groupid>

spring-beans</artifactid>

<version>4.1.7.RELEASE</version>

</dependency>

<dependency>

<groupid>org.springframework</groupid>

spring-context</artifactid>

<version>4.1.7.RELEASE</version>

</dependency>

<!-- 2)Spring DAO層 -->

<dependency>

<groupid>org.springframework</groupid>

spring-jdbc</artifactid>

<version>4.1.7.RELEASE</version>

</dependency>

<dependency>

<groupid>org.springframework</groupid>

spring-tx</artifactid>

<version>4.1.7.RELEASE</version>

</dependency>

<!-- 3)Spring web -->

<dependency>

<groupid>org.springframework</groupid>

spring-web</artifactid>

<version>4.1.7.RELEASE</version>

</dependency>

<dependency>

<groupid>org.springframework</groupid>

spring-webmvc</artifactid>

<version>4.1.7.RELEASE</version>

</dependency>

<!-- 4)Spring test -->

<dependency>

<groupid>org.springframework</groupid>

spring-test</artifactid>

<version>4.1.7.RELEASE</version>

</dependency>

<!-- redis客戶端:Jedis -->

<dependency>

<groupid>redis.clients</groupid>

jedis</artifactid>

<version>2.7.3</version>

</dependency>

<dependency>

<groupid>com.dyuproject.protostuff</groupid>

<artifactid>protostuff-core</artifactid>

<version>1.0.8</version>

</dependency>

<dependency>

<groupid>com.dyuproject.protostuff</groupid>

<artifactid>protostuff-runtime</artifactid>

<version>1.0.8</version>

</dependency>

<!-- Map工具類 -->

<dependency>

<groupid>commons-collections</groupid>

<artifactid>commons-collections</artifactid>

<version>3.2</version>

</dependency>

</dependencies>

<build>

<finalname>ssm</finalname>

</build>

</project>

下面真的要開始進行編碼工作了,堅持到這裡辛苦大家了~

第一步:我們先在spring資料夾裡新建spring-dao.xml檔案,因為spring的配置太多,我們這裡分三層,分別是dao service web。

1、讀入資料庫連線相關引數(可選)

2、配置資料連線池 

     配置連線屬性,可以不讀配置項檔案直接在這裡寫死

     配置c3p0,只配了幾個常用的

3、配置SqlSessionFactory物件(mybatis)

4、掃描dao層介面,動態實現dao介面,也就是說不需要daoImpl,sql和引數都寫在xml檔案上

spring-dao.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

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

<!-- 配置整合mybatis過程 -->

<!-- 1.配置資料庫相關引數properties的屬性:${url} -->

<context:property-placeholder location="classpath:jdbc.properties" />

<!-- 2.資料庫連線池 -->

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

<!-- 配置連線池屬性 -->

<property name="driverClass" value="${jdbc.driver}" />

<property name="jdbcUrl" value="${jdbc.url}" />

<property name="user" value="${jdbc.username}" />

<property name="password" value="${jdbc.password}" />

<!-- c3p0連線池的私有屬性 -->

<property name="maxPoolSize" value="30" />

<property name="minPoolSize" value="10" />

<!-- 關閉連線後不自動commit -->

<property name="autoCommitOnClose" value="false" />

<!-- 獲取連線超時時間 -->

<property name="checkoutTimeout" value="10000" />

<!-- 當獲取連線失敗重試次數 -->

<property name="acquireRetryAttempts" value="2" />

</bean>

<!-- 3.配置SqlSessionFactory物件 -->

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

<!-- 注入資料庫連線池 -->

<property name="dataSource" ref="dataSource" />

<!-- 配置MyBaties全域性配置檔案:mybatis-config.xml -->

<property name="configLocation" value="classpath:mybatis-config.xml" />

<!-- 掃描entity包 使用別名 -->

<property name="typeAliasesPackage" value="com.soecode.lyf.entity" />

<!-- 掃描sql配置檔案:mapper需要的xml檔案 -->

<property name="mapperLocations" value="classpath:mapper/*.xml" />

</bean>

<!-- 4.配置掃描Dao介面包,動態實現Dao介面,注入到spring容器中 -->

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

<!-- 注入sqlSessionFactory -->

<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />

<!-- 給出需要掃描Dao介面包 -->

<property name="basePackage" value="com.soecode.lyf.dao" />

</bean>

</beans>

因為資料庫配置相關引數是讀取配置檔案,所以在resources資料夾裡新建一個jdbc.properties檔案,存放我們4個最常見的資料庫連線屬性,這是我本地的,大家記得修改呀~還有喜歡傳到github上“大頭蝦們”記得刪掉密碼,不然別人就很容易得到你伺服器的資料庫配置資訊,然後幹一些羞羞的事情,你懂的!!

jdbc.properties

?

友情提示:配置檔案中的jdbc.username,如果寫成username,可能會與系統環境中的username變數衝突,所以到時候真正連線資料庫的時候,使用者名稱就被替換成系統中的使用者名稱(有得可能是administrator),那肯定是連線不成功的,這裡有個小坑,我被坑了一晚上!!

因為這裡用到了mybatis,所以需要配置mybatis核心檔案,在recources資料夾裡新建mybatis-config.xml檔案。

  • 使用自增主鍵
  • 使用列別名
  • 開啟駝峰命名轉換 create_time -> createTime

mybatis-config.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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

<!DOCTYPE configuration

PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

<configuration>

<!-- 配置全域性屬性 -->

<settings>

<!-- 使用jdbc的getGeneratedKeys獲取資料庫自增主鍵值 -->

<setting name="useGeneratedKeys" value="true" />

<!-- 使用列別名替換列名 預設:true -->

<setting name="useColumnLabel" value="true" />

<!-- 開啟駝峰命名轉換:Table{create_time} -> Entity{createTime} -->

<setting name="mapUnderscoreToCamelCase" value="true" />

</settings>

</configuration>

第二步:剛弄好dao層,接下來到service層了。在spring資料夾裡新建spring-service.xml檔案。

  • 掃描service包所有註解 @Service
  • 配置事務管理器,把事務管理交由spring來完成
  • 配置基於註解的宣告式事務,可以直接在方法上@Transaction

spring-service.xml

?

第三步:配置web層,在spring資料夾裡新建spring-web.xml檔案。

  • 開啟SpringMVC註解模式,可以使用@RequestMapping,@PathVariable,@ResponseBody等
  • 對靜態資源處理,如js,css,jpg等
  • 配置jsp 顯示ViewResolver,例如在controller中某個方法返回一個string型別的”login”,實際上會返回”/WEB-INF/login.jsp”
  • 掃描web層 @Controller

spring-web.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

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

<!-- 配置SpringMVC -->

<!-- 1.開啟SpringMVC註解模式 -->

<!-- 簡化配置:

(1)自動註冊DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter

(2)提供一些列:資料繫結,數字和日期的format @NumberFormat, @DateTimeFormat, xml,json預設讀寫支援

-->

<mvc:annotation-driven />

<!-- 2.靜態資源預設servlet配置

(1)加入對靜態資源的處理:js,gif,png

(2)允許使用"/"做整體對映

-->

<mvc:default-servlet-handler/>

<!-- 3.配置jsp 顯示ViewResolver -->

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />

<property name="prefix" value="/WEB-INF/jsp/" />

<property name="suffix" value=".jsp" />

</bean>

<!-- 4.掃描web相關的bean -->

<context:component-scan base-package="com.soecode.lyf.web" />

</beans>

第四步:最後就是修改web.xml檔案了,它在webapp的WEB-INF下。

web.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

version="3.1" metadata-complete="true">

<!-- 如果是用mvn命令生成的xml,需要修改servlet版本為3.1 -->

<!-- 配置DispatcherServlet -->

<servlet>

<servlet-name>seckill-dispatcher</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!-- 配置springMVC需要載入的配置檔案

spring-dao.xml,spring-service.xml,spring-web.xml

Mybatis - > spring -> springmvc

-->

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:spring/spring-*.xml</param-value>

</init-param>

</servlet>

<servlet-mapping>

<servlet-name>seckill-dispatcher</servlet-name>

<!-- 預設匹配所有的請求 -->

<url-pattern>/</url-pattern>

</servlet-mapping>

</web-app>

我們在專案中經常會使用到日誌,所以這裡還有配置日誌xml,在resources資料夾裡新建logback.xml檔案,所給出的日誌輸出格式也是最基本的控制檯s撥出,大家有興趣檢視logback官方文件

logback.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

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

<configuration debug="true">

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

<!-- encoders are by default assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder -->

<encoder>

<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>

</encoder>

</appender>

<root level="debug">

<appender-ref ref="STDOUT" />

</root>

</configuration>

到目前為止,我們一共寫了7個配置檔案,我們一起來看下最終的配置檔案結構圖。

SSM框架應用例項(圖書管理系統)

一開始想就這樣結束教程,但是發現其實很多人都還不會把這個SSM框架用起來,特別是mybatis部分。那我現在就以最常見的“圖書管理系統”中【查詢圖書】和【預約圖書】業務來做一個demo吧!

首先新建資料庫名為ssm,再建立兩張表:圖書表book和預約圖書表appointment,並且為book表初始化一些資料,sql如下。

schema.sql

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

-- 建立圖書表

CREATE TABLE `book` (

`book_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '圖書ID',

`name` varchar(100) NOT NULL COMMENT '圖書名稱',

`number` int(11) NOT NULL COMMENT '館藏數量',

PRIMARY KEY (`book_id`)

) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='圖書表'

-- 初始化圖書資料

INSERT INTO `book` (`book_id`, `name`, `number`)

VALUES

(1000, 'Java程式設計', 10),

(1001, '資料結構', 10),

(1002, '設計模式', 10),

(1003, '編譯原理', 10)

-- 建立預約圖書表

CREATE TABLE `appointment` (

`book_id` bigint(20) NOT NULL COMMENT '圖書ID',

`student_id` bigint(20) NOT NULL COMMENT '學號',

`appoint_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '預約時間' ,

PRIMARY KEY (`book_id`, `student_id`),

INDEX `idx_appoint_time` (`appoint_time`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='預約圖書表'

在entity包中新增兩個對應的實體,圖書實體Book.java和預約圖書實體Appointment.java。

Book.java

?

1

2

3

4

5

6

7

8

9

10

11

12

13

package com.soecode.lyf.entity;

public class Book {

private long bookId;// 圖書ID

private String name;// 圖書名稱

private int number;// 館藏數量

// 省略構造方法,getter和setter方法,toString方法

}

Appointment.java

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

package com.soecode.lyf.entity;

import java.util.Date;

/**

* 預約圖書實體

*/

public class Appointment {

private long bookId;// 圖書ID

private long studentId;// 學號

private Date appointTime;// 預約時間

// 多對一的複合屬性

private Book book;// 圖書實體

// 省略構造方法,getter和setter方法,toString方法

}

在dao包新建介面BookDao.java和Appointment.java

BookDao.java

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

package com.soecode.lyf.dao;

import java.util.List;

import com.soecode.lyf.entity.Book;

public interface BookDao {

/**

* 通過ID查詢單本圖書

*

* @param id

* @return

*/

Book queryById(long id);

/**

* 查詢所有圖書

*

* @param offset 查詢起始位置

* @param limit 查詢條數

* @return

*/

List<Book> queryAll(@Param("offset") int offset, @Param("limit") int limit);

/**

* 減少館藏數量

*

* @param bookId

* @return 如果影響行數等於>1,表示更新的記錄行數

*/

int reduceNumber(long bookId);

}

AppointmentDao.java

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

package com.soecode.lyf.dao;

import org.apache.ibatis.annotations.Param;

import com.soecode.lyf.entity.Appointment;

public interface AppointmentDao {

/**

* 插入預約圖書記錄

*

* @param bookId

* @param studentId

* @return 插入的行數

*/

int insertAppointment(@Param("bookId") long bookId, @Param("studentId") long studentId);

/**

* 通過主鍵查詢預約圖書記錄,並且攜帶圖書實體

*

* @param bookId

* @param studentId

* @return

*/

Appointment queryByKeyWithBook(@Param("bookId") long bookId, @Param("studentId") long studentId);

}

提示:這裡為什麼要給方法的引數新增@Param註解呢?是因為該方法有兩個或以上的引數,一定要加,不然mybatis識別不了。上面的BookDao介面的queryById方法和reduceNumber方法只有一個引數book_id,所以可以不用加 @Param註解,當然加了也無所謂~

注意:這裡不需要實現dao介面不用編寫daoImpl, mybatis會給我們動態實現,但是我們需要編寫相應的mapper。 
在mapper目錄裡新建兩個檔案BookDao.xml和AppointmentDao.xml,分別對應上面兩個dao介面,程式碼如下。

BookDao.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

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

<!DOCTYPE mapper

PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

<mapper namespace="com.soecode.lyf.dao.BookDao">

<!-- 目的:為dao介面方法提供sql語句配置 -->

<select id="queryById" resultType="Book" parameterType="long">

<!-- 具體的sql -->

SELECT

book_id,

name,

number

FROM

book

WHERE

book_id = #{bookId}

</select>

<select id="queryAll" resultType="Book">

SELECT

book_id,

name,

number

FROM

book

ORDER BY

book_id

LIMIT #{offset}, #{limit}

</select>

<update id="reduceNumber">

UPDATE book

SET number = number - 1

WHERE

book_id = #{bookId}

AND number > 0

</update>

</mapper>

AppointmentDao.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

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

<!DOCTYPE mapper

PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

<mapper namespace="com.soecode.lyf.dao.AppointmentDao">

<insert id="insertAppointment">

<!-- ignore 主鍵衝突,報錯 -->

INSERT ignore INTO appointment (book_id, student_id)

VALUES (#{bookId}, #{studentId})

</insert>

<select id="queryByKeyWithBook" resultType="Appointment">

<!-- 如何告訴MyBatis把結果對映到Appointment同時對映book屬性 -->

<!-- 可以自由控制SQL -->

SELECT

a.book_id,

a.student_id,

a.appoint_time,

b.book_id "book.book_id",

b.`name` "book.name",

b.number "book.number"

FROM

appointment a

INNER JOIN book b ON a.book_id = b.book_id

WHERE

a.book_id = #{bookId}

AND a.student_id = #{studentId}

</select>

</mapper>

mapper總結:namespace是該xml對應的介面全名,select和update中的id對應方法名,resultType是返回值型別,parameterType是引數型別(這個其實可選),最後#{...}中填寫的是方法的引數,看懂了是不是很簡單!!我也這麼覺得~ 還有一個小技巧要交給大家,就是在返回Appointment物件包含了一個屬性名為book的Book物件,那麼可以使用"book.屬性名"的方式來取值,看上面queryByKeyWithBook方法的sql。

dao層寫完了,接下來test對應的package寫我們測試方法吧。 

因為我們之後會寫很多測試方法,在測試前需要讓程式讀入spring-dao和mybatis等配置檔案,所以我這裡就抽離出來一個BaseTest類,只要是測試方法就繼承它,這樣那些繁瑣的重複的程式碼就不用寫那麼多了~

BaseTest.java

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

package com.soecode.lyf;

import org.junit.runner.RunWith;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**

* 配置spring和junit整合,junit啟動時載入springIOC容器 spring-test,junit

*/

@RunWith(SpringJUnit4ClassRunner.class)

// 告訴junit spring配置檔案

@ContextConfiguration({ "classpath:spring/spring-dao.xml", "classpath:spring/spring-service.xml" })

public class BaseTest {

}

因為spring-service在service層的測試中會時候到,這裡也一起引入算了!

新建BookDaoTest.java和AppointmentDaoTest.java兩個dao測試檔案。

BookDaoTest.java

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

package com.soecode.lyf.dao;

import java.util.List;

import org.junit.Test;

import org.springframework.beans.factory.annotation.Autowired;

import com.soecode.lyf.BaseTest;

import com.soecode.lyf.entity.Book;

public class BookDaoTest extends BaseTest {

@Autowired

private BookDao bookDao;

@Test

public void testQueryById() throws Exception {

long bookId = 1000;

Book book = bookDao.queryById(bookId);

System.out.println(book);

}

@Test

public void testQueryAll() throws Exception {

List<Book> books = bookDao.queryAll(0, 4);

for (Book book : books) {

System.out.println(book);

}

}

@Test

public void testReduceNumber() throws Exception {

long bookId = 1000;

int update = bookDao.reduceNumber(bookId);

System.out.println("update=" + update);

}

}

BookDaoTest測試結果

testQueryById

testQueryById

testQueryAll 

testReduceNumber 

AppointmentDaoTest.java

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

package com.soecode.lyf.dao;

import org.junit.Test;

import org.springframework.beans.factory.annotation.Autowired;

import com.soecode.lyf.BaseTest;

import com.soecode.lyf.entity.Appointment;

public class AppointmentDaoTest extends BaseTest {

@Autowired

private AppointmentDao appointmentDao;

@Test

public void testInsertAppointment() throws Exception {

long bookId = 1000;

long studentId = 12345678910L;

int insert = appointmentDao.insertAppointment(bookId, studentId);

System.out.println("insert=" + insert);

}

@Test

public void testQueryByKeyWithBook() throws Exception {

long bookId = 1000;

long studentId = 12345678910L;

Appointm