MyBatis入門實例:整合Spring MVC與MyBatis開發問答網站

CiaraSchnel 8年前發布 | 38K 次閱讀 MyBatis MyBatis3 持久層框架 Spring MVC

開發目標

今天我們使用MyBatis和Spring MVC開發一個簡單的問答網站,你可以使用Markdown來發布問題和答案。

這篇文章可以教會你以下知識:

  • MyBatis配置和使用最簡單的姿勢
  • MyBatis+Spring MVC實現一個簡單的Web頁面
  • MyBatis的核心類是干嘛用的(SqlSessionFactoryBuilder、SqlSessionFactory和SqlSession)
  • MyBatis-Spring如何使用,幫你做了哪些工作?
  • MyBatis Spring Boot Starter如何支持MyBatis,讓你使用MyBatis的門檻降到最低

相信看完本身是你開始使用MyBatis的一個非常棒的開始!

最終的效果圖如下:

Web端的開發我們使用Spring MVC,對Spring MVC還不太熟悉的同學,可以看看天碼營的 Spring MVC實戰練習 。當然,Spring MVC的部分不是這個練習的重點,也基本不影響大家的理解和練習。

這樣一個系統麻雀雖小,五張俱全,會涉及MyBatis的很多核心知識。MyBatis確實是一個非常簡單易學的ORM框架,很適合作為你學習的第一款Java ORM框架。

ORM是什么?

ORM是Object Relation Mapping的縮寫,顧名思義,即對象關系映射。

ORM是一種以面向對象的方式來進行數據庫操作的技術。Web開發中常用的語言,都會有對應的ORM框架。而MyBatis就是Java開發中一種常用ORM框架。

簡單地理解,通過Java進行數據庫訪問的正常流程可以分為以下幾步:

  1. 準備好SQL語句
  2. 調用JDBC的API傳入SQL語句,設置參數
  3. 解析JDBC返回的結果

這個過程實際上非常麻煩,比如:

  • 在Java代碼中拼接SQL非常麻煩,而且易于出錯
  • JDBC的代碼調用有很多重復性的代碼
  • 從JDBC返回的結果轉換成領域模型的Java對象很繁瑣

而使用ORM框架,則可以讓我們用面向對象的方式來操作數據庫,比如通過一個簡單的函數調用就完成上面整個流程,直接返回映射為Java對象的結果。這個流程中很大一部分工作其實交給ORM自動化地幫我們執行了。

MyBatis簡介

MyBatis的入門知識最好的材料是其 官方網站 ,而且其絕大部分內容都有中文版本。

官方網站上如下介紹MyBatis:

MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以對配置和原生Map使用簡單的 XML 或標注,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。

簡單地理解,你可以認為MyBatis將SQL語句,以及SQL返回結果到Java對象的映射,都放到了一個易于配置的XML文件里了,你的Java代碼就會變得異常簡單。當然,除了XML,MyBatis同時也支持基于標注的方式,但是功能上會有一些限制。總體來說,我們推薦使用XML方式,一些簡單的SQL使用標注會更方便一些。

開發環境

工欲善其事,必先利其器

首先讓我們搭建好本地的開發環境,這里不會事無巨細地描述環境中每一種工具的安裝步驟和用法,你可以從參考材料以及Google中獲取有用的信息。

Java

我們推薦安裝 JavaSE Development Kit 8

參考 Java開發環境安裝與配置

IDE

IDE我們推薦使用 Eclipse IntelliJ IDEA (當然還有很多別的選擇),它們對Maven項目的支持非常完善,自動提示、補全功能異常強大,對于開發效率的提升非常明顯。

參考 Eclipse安裝和使用

Maven

Maven是Java世界中最流行的項目構建工具,理論上來說在安裝了IDE后,IDE內部會自帶一個Maven的安裝版本,如果想在命令行工具中使用Maven命令,可以單獨進行安裝。

參考:

如果想深入了解,推薦 Maven實戰

H2

我們使用內嵌的數據庫H2,如果希望轉換成其他數據庫,比如MySQL,只需修改數據庫連接串即可。當然別忘了要在依賴中增加相應的數據庫訪問驅動包。H2數據庫不需要你任何額外的安裝工作。

創建項目

接下來我們開始創建第一個MyBatis項目吧。新建一個Maven項目,項目結構如下:

.
├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │       └── com
│   │           └── tianmaying

接下來引入MyBatis和H2的依賴:

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.0</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.192</version>
    <scope>runtime</scope>
</dependency>

既然是ORM的練習,那么我們先設計Object,在設計Relation,最后來來看怎么做Mapping。

領域類設計

關于領域類設計, 貪吃蛇的游戲的練習 中有一個簡單講解,請參考 如何開始面向對象設計 。

分析我們的業務場景,可以設計三個類:

  • Question表示問題
  • Answer表示回答
  • Tag表示問題標簽

其中:

  • Question可以有多個Answer,一個Answer對應唯一一個Question,一對多的關系
  • Question可以有多個Tage,一個Tag可用于多個Question,多對多的關系

這三個類的關系用UML圖如下:

UML中用菱形+橫線的方式,表示包含關系。同樣是包含,大家注意左側的菱形是填充的,而右側沒有填充,這兩者的區別在于:

  • Question消失時,Answer必然也消失了,它們有相同的生命周期,皮之不存,毛將焉附嘛
  • Question消失時,Tag則可以仍然存在,因為Tag還可以標注其他問題

現在我們暫時不管Answer和Tag,只關注Question,先從最簡單的單表情況開始學習。Question的代碼如下:

public class Question implements Serializable {

private Long id;
private String title;
private String description;
private Date createdTime;
private List<Tag> tags;
private List<Answer> answers;

}</code></pre>

這里Question實現了Serializable接口,在這一節的練習中不是必須的。實現這個接口是為了后面緩存查詢結果是需要。

數據庫設計

除了設計用于Question,Answer和Tag數據的表,我們還需要定義一張維護Question和Tag之間多對多關系的表。最終數據庫設計如下:

這個階段你只需關注question表即可。

添加MyBatis的配置

接下來我們添加MyBatis配置,在resources目錄下建立mybatis.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        ";

<configuration>

<properties>
    <property name="url" value="jdbc:h2:mem:dataSource;INIT=RUNSCRIPT FROM 'classpath:database/schema.sql'\;RUNSCRIPT FROM 'classpath:database/data.sql'" />
</properties>

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="org.h2.Driver"/>
            <property name="url" value="${url}"/>
        </dataSource>
    </environment>
</environments>
<mappers>
    <mapper resource="com/tianmaying/qa/mapper/QuestionMapper.xml"/>
</mappers>

</configuration></code></pre>

這就是一個最簡單的MyBatis配置文件,定義了數據源和mapper文件的位置。

注意H2的連接串中執行了創建數據庫表和插入初始化數據的腳本,天碼營已經為你準備了一些數據。

關于MyBatis配置

后面你會發現有了Spring和Spring Boot支持,很多配置不需要我們手動設置了,比如映射文件位置、數據源和事務管理器等。這個文件需要的內容非常少,甚至可以不需要這個文件了。

需要修改MyBatis配置文件的幾種常見情況包括:

  • 要增加插件(比如后面我們看到的分頁插件)
  • 修改MyBatis的運行時行為,參考settings的選項
  • 重寫類型處理器或創建自定義的類型處理器來處理非標準的類型,天碼營的這個練習中不會涉及

<mapper resource="com/tianmaying/qa/mapper/QuestionMapper.xml"/>中表示com/tianmaying/qa/mapper/目錄下的QuestionMapper.xml是定義對象關系映射的XML文件,馬上就能看到它長什么樣子。

定義Mapper接口

先來定義Mapper的Java接口,這是我們數據庫訪問的接口:

package com.tianmaying.qa.mapper;

import com.tianmaying.qa.model.Question; import org.apache.ibatis.annotations.Param;

public interface QuestionMapper {

Question findOne(@Param("id") Long id);

}</code></pre> <p>@Param是MyBatis提高的一個標注,表示id會解析成SQL語句(SQL語句會在XML配置或者標注中)的參數。</p>

使用XML定義Mapper

對應于Mapper接口,還需要通過XML來給出Mapper的實現。我們將映射文件一般放在resources目錄下的com/tianmaying/qa/mapper/子目錄(這是一個四層目錄,與Mapper的包名一致)。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
";

<mapper namespace="com.tianmaying.qa.mapper.QuestionMapper"> <cache />

<select id="findOne" resultType="com.tianmaying.qa.model.Question"> SELECT * FROM question WHERE question.id = #{id} </select>

</mapper></code></pre>

Mapper的配置是MyBatis的精華,我們暫時不做詳解。這里你注意兩點即可:

  • 對應于每一個Mapper的Java接口方法,XML配置中有對應的一個元素來描述其SQL語句
  • resultMap元素定義了數據庫返回(一行記錄)如何映射到一個Java對象

使用Mapper

已經有了一個可以根據id獲取問題的接口方法,接下來就可以進行調用了:

public static void main(String[] args) {

// 準備工作
InputStream inputStream = null;
try {
    // CONFIG_LOCATION的值即為MyBatis配置文件的路徑
    inputStream = Resources.getResourceAsStream(CONFIG_LOCATION);
} catch (IOException e) {
    e.printStackTrace();
}

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

try {
    // 獲取mapper            
    QuestionMapper questionMapper = sqlSession.getMapper(QuestionMapper.class);
    // 調用mapper方法
    Question question = questionMapper.findOne((long) 1);
    System.out.println(question);
} finally {
    // 最后一定關閉SqlSession
    sqlSession.close();
}

}</code></pre>

把這個Main方法Run起來你就能看到結果了。調用看起來有點復雜,這里涉及MyBatis的幾個關鍵類:

  • SqlSessionFactoryBuilder
  • SqlSessionFactory
  • SqlSession

其實后面我們會引入Spring Boot,你會發現這幾個類都被屏蔽掉了,你只需專注于寫Mapper即可。不過了解一下這幾個類,對于我們調試程序和理解MyBatis都是大有裨益的。

引入Spring Boot

上一個練習我們了解了MyBatis的基本用法,但是一個控制臺應用顯然不夠酷,我們接下來就開始在Web應用中來使用MyBatis吧。

首先我們引入Spring Boot,為項目的POM文件添加一個父POM,在POM文件中的project根元素中添加:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.1.RELEASE</version>
    <relativePath/> 
</parent>

然后添加以下依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>net.sourceforge.nekohtml</groupId>
    <artifactId>nekohtml</artifactId>
</dependency>

關于Spring Boot請參考以下資料:

編寫Controller和頁面

我們使用Spring MVC和Thymeleaf來實現前端頁面。

這里我們先寫出Controller的代碼骨架:

@Controller
public class QuestionController {

@GetMapping("/{id}")
public String showQuestion(@PathVariable Long id, Model model) {
    // 1. 獲取Mapper
    // 2. 調用Mapper方法獲取Question 
    // 3. 填充用以渲染頁面的模型,這里即是Question實例
    model.addAttribute("question", question);

    // 返回模板名稱
    return "question";
}

}</code></pre>

Thymeleaf的頁面細節這里不再詳述。我們把上節練習中main函數的代碼放到Controller中來就基本完成一個Web頁面的開發了。

不過這里我們可以做一些優化,而不是簡單把代碼搬過來用。優化的過程中來進一步理解MyBatis的幾個核心類。

SqlSessionFactory和SelSessionFactoryBuilder

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder這個類是用來創建SqlSessionFactory的,一旦創建了SqlSessionFactory實例,就不再需要它了。因此SqlSessionFactoryBuilder 實例的最佳范圍是方法范圍(也就是局部方法變量)。可以重用SqlSessionFactoryBuilder 來創建多個 SqlSessionFactory 實例,但是最好不要讓其一直存在,比如通過一個類的成員去引用是不推薦的。

SqlSessionFactory

SqlSessionFactory一旦被創建就應該在應用的運行期間一直存在,沒有任何理由對它進行清除或重建。使用SqlSessionFactory的最佳實踐是在應用運行期間不要重復創建多次,多次重建SqlSessionFactory被視為一種代碼“壞味道(bad smell)”。因此SqlSessionFactory的最佳范圍是應用范圍。有很多方法可以做到,最簡單的就是使用單例模式或者靜態單例模式。

通過單例創建SqlSessionFactory

可見,SqlSessionFactoryBuilder應該用完即扔,而SqlSessionFactory應該只創建一次且在整個應用內存在。因此我們這里使用單例模式來獲得SqlSessionFactory實例。

package com.tianmaying.qa.mapper;

import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException; import java.io.InputStream;

public class SqlSessionFactoryManager {

private static final String CONFIG_LOCATION = "mybatis.xml";

private static SqlSessionFactory sqlSessionFactory;

// 靜態單例模式
public static SqlSessionFactory getSqlSessionFactory() {
    if (sqlSessionFactory == null) {
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(CONFIG_LOCATION);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        // ** SqlSessionFactoryBuilder用完即扔
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    return sqlSessionFactory;
}

}</code></pre>

基于這樣一個輔助類,獲取應用的SqlSessionFactory實例可以如下方式:

SqlSessionFactory sqlSessionFactory = SqlSessionFactoryManager.getSqlSessionFactory();

SqlSession

SqlSession實例不是線程安全的,因此是不能被共享的,所以它的最佳的范圍是請求或方法范圍。不能將SqlSession實例的引用放在一個類的靜態域,甚至一個類的實例變量也不行。也不能將SqlSession實例的引用放在任何類型的管理范圍中,比如Serlvet中的HttpSession。如果你現在正在使用一種 Web 框架,要考慮 SqlSession 放在一個和 HTTP 請求對象相似的范圍中。換句話說,每次收到的 HTTP 請求,就可以打開一個 SqlSession,返回一個響應,就關閉它。這個關閉操作是很重要的,應該把這個關閉操作放到 finally 塊中以確保每次都能執行關閉。下面的示例就是一個確保 SqlSession 關閉的標準模式:

SqlSession session = sqlSessionFactory.openSession();
try {
  // do work
} finally {
  session.close();
}

了解了以上三個類之后,Controller最終的代碼實現為:

@Controller
public class QuestionController {

@GetMapping("/{id}")
public String showQuestion(@PathVariable Long id, Model model) {
    SqlSession sqlSession = SqlSessionFactoryManager.getSqlSessionFactory().openSession();

    try {
        QuestionMapper questionMapper = sqlSession.getMapper(QuestionMapper.class);
        model.addAttribute("question", questionMapper.findOne(id));
        return "question";
    } finally {
        sqlSession.close();
    }
}

}</code></pre>

啟動Spring Boot應用

修改MybatisQaApplication的代碼,使之成為一個Spring Boot應用:

@SpringBootApplication
@Controller
public class MybatisQaApplication {

public static void main(String[] args) {
    SpringApplication.run(MybatisQaApplication.class, args);
}

}</code></pre>

在resources目錄下增加一個應用配置文件application.properties,內容為:

spring.thymeleaf.mode=LEGACYHTML5

這一行配置是為了讓Thymeleaf支持HTML5。

此時你在命令行或者IDE中運行mvn spring-boot:run就可以啟動應用,訪問http:localhost:8080/1這樣的URL就能獲取id為1的問題的詳細信息了。

什么是MyBatis-Spring

MyBatis官方文檔中這樣介紹 MyBatis-Spring :

MyBatis-Spring 會幫助你將 MyBatis 代碼無縫地整合到 Spring 中。 使用這個類庫中的類, Spring 將會加載必要的 MyBatis 工廠類和 session 類。 這個類庫也提供一個簡單的方式來注入 MyBatis 數據映射器和 SqlSession 到業務層的 bean 中。 而且它也會處理事務, 翻譯 MyBatis 的異常到 Spring 的 DataAccessException 異常(數據訪問異常,譯者注)中。最終,它不會依賴于 MyBatis,Spring 或 MyBatis-Spring 來構建應用程序代碼。

總之MyBatis-Spring幫我們做了很多事情,最核心的有兩點:

  • Spring 將會加載必要的 MyBatis 工廠類和 session 類:這意味著不需要我們自己去創建SqlSessionFactory實例和SqlSession實例了,幫我們從MyBatis底層API中解放出來啦

  • 提供一個簡單的方式來注入 MyBatis 數據映射器和 SqlSession 到業務層的 bean 中:使用Spring當然要使用依賴注入了,不需要我們自己手動創建Mapper了,直接注入即可

如何理解下面這句話呢?

最終,它不會依賴于 MyBatis,Spring 或 MyBatis-Spring 來構建應用程序代碼。

正因為MyBatis-Spring幫我們做了這些事,我們的業務代碼就可以完全屏蔽MyBatis的API了,對應于Controller的代碼,直觀上理解就是你將不會看到關于MyBatis相關類的import了。

來看實例吧。

引入MyBatis-Spring

要使用MyBatis-Spring模塊,只需在POM文件中引入必要的依賴即可,這里我們引入1.3.0版本:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>1.3.0</version>
</dependency>

XML配置

MyBatis-Spring引入了SqlSessionFactoryBean來創建SqlSessionFactory,這是一個工廠Bean,在XML中如下配置:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>

注意SqlSessionFactoryBean不是MyBatis本身的類,是MyBatis-Spring這個模塊引入的類,還記得上一個練習中我們直接使用MyBatis時,是通過SqlSessionFactoryBuilder來創建SqlSessionFactory的吧。

SqlSessionFactoryBean必須設置一個數據源,數據源的配置方式和普通的Spring應用沒有任何區別。

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  
    destroy-method="close">  
    <property name="driverClassName" value="org.h2.Driver" />  
    <property name="url"  
        value="jdbc:h2:mem:dataSource;INIT=RUNSCRIPT FROM 'classpath:database/schema.sql'\;RUNSCRIPT FROM 'classpath:database/data.sql'" />  
</bean>

配置好SqlSessionFactoryBean之后,可以配置Mapper成為Spring Bean,這樣就能在Controller中使用了。

<bean id="questionMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="com.tianmaying.qa.QuestionMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

mapperInterface屬性所指定的映射器類必須是一個接口,不能是一個具體的實現類,QuestionMapper顯然符合這個要求。

這樣在Controller中就可以使用questionMapper這個Bean了。

Java配置

上一小節是通過XML來配置數據源、SqlSessionFactoryBean和Mapper。我們也可以通過Java來進行配置。

這里我們創建一個AppConfig配置類來專門放置這些配置代碼:

package com.tianmaying.qa;

import com.tianmaying.qa.mapper.QuestionMapper; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.mapper.MapperFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

import javax.sql.DataSource;

@Configuration public class AppConfig {

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript("classpath:database/schema.sql")
            .addScript("classpath:database/data.sql")
            .build();
}

@Bean
public SqlSessionFactory sqlSessionFactory() {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource());

    try {
        return sqlSessionFactoryBean.getObject();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

@Bean
public MapperFactoryBean questionMapperFactoryBean() {
    MapperFactoryBean mapperFactoryBean = new MapperFactoryBean(QuestionMapper.class);
    mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory());
    return mapperFactoryBean;
}

}</code></pre>

在Java配置中要注意工廠方法的Bean的寫法,要通過調用getObject()返回目標Bean,而不是創建目標的工廠Bean。

使用Java配置時,為了使用EmbeddedDatabaseBuilder,你需要在POM文件中增加Spring JDBC相關的依賴,這里我們把JDBC Starter加入進來即可:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

Controller層的代碼

此時Controller的代碼變得前所未有的簡單:

@Controller
public class QuestionController {

@Autowired
QuestionMapper questionMapper;

@GetMapping("/{id}")
public String showQuestion(@PathVariable Long id, Model model) {
    model.addAttribute("question", questionMapper.findOne(id));
    return "question";
}

}</code></pre>

你再也看不到一堆創建SqlSessionFactory和SqlSession的代碼了,MyBatis-Spring都已經幫你做了,你要做的就是自動裝配一個Mapper,然后調用方法操作數據庫。

所以你現在應該更清楚為什么官方文檔這么說了吧:

最終,它不會依賴于 MyBatis,Spring 或 MyBatis-Spring 來構建應用程序代碼。

SqlSessionFactoryManager這個類此時也沒有存在的必要了。

關于SqlSessionFactoryBean

SessionFactoryBean還有一個屬性是configLocation,它是用來指定MyBatis的XML配置文件路徑的。

如果你希望修改MyBatis的一些基礎配置,比如你希望修改Mybatis配置文件中的<settings> 或<typeAliases>部分,那么修改后的配置文件的路徑要在configLocation屬性中設置。

比如為了啟動延遲加載,可以如下配置:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="mapperLocations" value="classpath*:classpath:mybatis-config.xml" />
</bean>

與此同時,在mybatis-config.xml中要進行進行lazyLoadingEnabled設置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        ";

<configuration>

<properties>
    <property name="url" value="jdbc:h2:mem:dataSource;INIT=RUNSCRIPT FROM 'classpath:database/schema.sql'\;RUNSCRIPT FROM 'classpath:database/data.sql'" />
</properties>

 <!-- **  注意這里設置了lazyLoadingEnabled -->
<settings>
  <setting name="lazyLoadingEnabled" value="true"/>
</settings>

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="org.h2.Driver"/>
            <property name="url" value="${url}"/>
        </dataSource>
    </environment>
</environments>
<mappers>
    <mapper resource="com/tianmaying/qa/mapper/QuestionMapper.xml"/>
</mappers>

</configuration></code></pre>

另一種方式是,你可以直接設置configuation屬性,下面的配置和前面的做法是等價的,好處是不用修改MyBatis配置文件。

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="configuration">
    <bean class="org.apache.ibatis.session.Configuration">
      <property name="lazyLoadingEnabled" value="true"/>
    </bean>
  </property>
</bean>

這些配置你在這個練習中并不會涉及。你需要注意的一點是:MyBatis的配置文件中的數據源和事務相關的配置將會被MyBatis-Spring忽略,即MyBatis XML配置中的<enviroment>元素會被忽略,MyBatis-Spring只會使用Spring Context下定義的數據源和事務管理器。

MyBatis Spring Boot Starter

軟件技術發展的一個驅動力之一就是不斷屏蔽底層的復雜性,使用更自然更易于理解的模型。這樣一個道理從我們這個簡單的練習課程就可見一斑,比如:

  • 最早的數據存儲技術是直接讀寫文件,為了屏蔽物理數據讀寫的復雜性,出現了數據庫技術;
  • 為了支持使用更自然的面向對象模型來訪問數據庫,出現了第一個練習中介紹的ORM框架,比如MyBatis;
  • 為了屏蔽MyBatis底層的API,MyBatis-Spring出現,讓使用MyBatis的代碼更加易于維護,第二個練習中你可以發現Controller的代碼因為MyBatis-Spring變得更加簡潔了;
  • 即使使用MyBatis-Spring,我們發現還需做一些麻煩的事情,比如配置數據源、 配置SqlSessionFactoryBean和配置映射器(Mapper)等工作;如何屏蔽這些麻煩的事情呢,這就需要MyBatis Spring Boot Stater粉墨登場了!

其實再往大了說,人類就是不斷發明和創造出更好的工具來生存和發展的。細到程序員這個群體也是,我們非常“懶”,遇到"不爽"的地方,就會出現更新更好的技術、平臺、工具或者框架來解決問題。

Spring Boot的Starter可以認為是一種方便的依賴描述符,需要集成某種框架或者功能(如集成JPA、Thymeleaf或者MongoDB)時,無需關注各種依賴庫的處理,無需具體的配置信息,由Spring Boot自動掃描類路徑創建所需的Bean。比如把spring-boot-starter-data-jpa的依賴添加到POM文件中,你就可以方便地使用JPA進行數據庫訪問了。

MyBatis Spring Boot Starter可以使得你使用MyBatis變得無比簡單,比如:

  • 自動掃描類路徑下數據庫驅動類,創建DataSouce實例
  • 基于DataSource自動創建SqlSessionFactoryBean實例
  • 自動掃描映射器相關類,生成所需要Mapper實例

總是呢,就是自動幫你做了很多事,讓你開發更加輕松愜意。

引入MyBatis Spring Boot Starter

添加一個依賴即可引入MyBatis Spring Boot Starter:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>

有了這個依賴,MyBatis和MyBatis-Spring的依賴都不再需要,他們會通過mybatis-spring-boot-starter間接地引入。

此外還需在application.properties中添加幾項配置:

spring.datasource.schema=classpath:database/schema.sql
spring.datasource.data=classpath:database/data.sql
mybatis.config-location=classpath:mybatis.xml

相比于原來的數據源配置:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  
    destroy-method="close">  
    <property name="driverClassName" value="org.h2.Driver" />  
    <property name="url"  
        value="jdbc:h2:mem:dataSource;INIT=RUNSCRIPT FROM 'classpath:database/schema.sql'\;RUNSCRIPT FROM 'classpath:database/data.sql'" />  
</bean>

已經得到了很大的簡化,而且H2數據庫初始化的語法也幫我們屏蔽了。

此外,mybatis.config-location=classpath:mybatis.xml在仍然還需要引用MyBatis的配置文件的情況下需要進行設置。也就是說修改MyBatis本身的一些行為,你還是需要借助于MyBatis的配置文件的,那么為了讓MyBatis Spring Boot Starter知道你對配置文件的修改,你需要做這個設置。

事實上在最后一次練習之前,這個配置基本都是不需要的。

更簡潔的代碼

為了讓Spring Boot能夠自動掃描到Mapper類,為其創建Bean實例,只需給Mapper添加@Mapper標注。

@Mapper
public interface QuestionMapper {
    Question findOne(@Param("id") Long id);
}

可以發現,MyBatis-Spring的引入消除了SqlSessionFactoryManager類,而這一次Starter的引入則使得AppConfig整個配置類(或者對應的Spring XML配置)此時也不再需要。

回顧一下AppConfig這個類,這里面做的所有事情Spring Boot都已經幫我們做好了,這也可以讓我們更清楚第一小節中所說的Spring Boot Starter的引入如何簡化開發。

package com.tianmaying.qa;

import com.tianmaying.qa.mapper.QuestionMapper; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.mapper.MapperFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

import javax.sql.DataSource;

@Configuration public class AppConfig {

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript("classpath:database/schema.sql")
            .addScript("classpath:database/data.sql")
            .build();
}

@Bean
public SqlSessionFactory sqlSessionFactory() {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource());

    try {
        return sqlSessionFactoryBean.getObject();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

@Bean
public MapperFactoryBean questionMapperFactoryBean() {
    MapperFactoryBean mapperFactoryBean = new MapperFactoryBean(QuestionMapper.class);
    mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory());
    return mapperFactoryBean;
}

}</code></pre>

 

來自:https://zhuanlan.zhihu.com/p/23517856

 

 本文由用戶 CiaraSchnel 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!