Spring Boot 項目整合 MyBatis 連接數據庫

在我們日常的開發過程中,肯定不可避免的會使用到數據庫以及 SQL 語句。比如,剛開始學習 Java 的時候可能會遇到 JDBC,它是連接 Java 和數據庫的橋樑,我們可以使用 JDBC 來建立與數據庫之間的連接並且執行相應的 SQL 語句。雖然 JDBC 的執行效率很高,但是其開發效率比較低。正是如此,市面上出現了一大批 ORM(對象關係映射)框架,例如 Hibernate,iBATIS 以及本文將要介紹的 MyBatis。

MyBatis 簡介

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

通俗地說,MyBatis 就是我們使用 Java 程序操作數據庫時的一種工具,可以簡化我們使用 JDBC 時的很多操作,而且還簡化了數據庫記錄與 POJO 之間的映射方式。

爲什麼要使用 MyBatis

前文提到過,目前市面上 有很多的 ORM 框架,例如 Hibernate,iBATIS 以及 Spring 全家桶的 Spring Data JPA。那麼我們爲什麼要使用 MyBatis 呢?因爲使用 MyBatis 有以下優勢:

可以簡化我們操作數據庫的步驟。

相對 Hibernate 來說學習成本較低,Hibernate 還需要學習其 HQL 查詢規範。

使用相對廣泛。

準備工作

本文將使用開源的數據庫連接池 DBCP(DataBase Connection Pool)連接 MySQL 數據庫,並在此基礎上整合 MyBatis 以及 MyBatis Plus,進而講解如何使用 MyBatis 和 MyBatis Plus 操作數據庫。所以在開始本教程的閱讀之前,需要如下準備:

一個 Spring Boot 的 Web 項目,你可以通過 Spring Initializr 頁面生成一個空的 Spring Boot 項目,當然也可以下載 springboot-pom.xml 文件,然後使用 Maven 構建一個 Spring Boot 項目。

安裝 MySQL 數據庫或者一臺 MySQL 服務器。

使用 DBCP 連接池

DBCP 數據庫連接池是 Apache 上的一個 Java 連接池項目,也是 Tomcat 使用的連接池組件。由於建立數據庫連接是一種非常耗時、耗資源的行爲,所以通過連接池預先同數據庫建立一些連接,放在內存中,應用程序需要建立數據庫連接時直接到連接池中申請一個就行,使用完畢後再歸還到連接池中。

添加依賴

這一步很簡單,只需要在 pom.xml 中添加清單 1 的內容即可。

清單 1. 添加相關依賴

<!--連接驅動-->mysqlmysql-connector-java6.0.6runtimeorg.apache.commonscommons-dbcp22.5.0< /dependency>

配置數據源

添加好依賴後,我們需要做的就是配置我們的數據源。首先我們需要在配置文件中添加數據源相關的配置項的值,下面清單代碼只給出了部分項,完整內容可以查看本文源碼:

清單 2. 數據源配置文件配置項

# 基本屬性spring:datasource:dbcp2:url: jdbc:mysql://localhost:3306/spring_tutorial?serverTimezone=GMT%2B8amp;characterEncoding=utf-8username: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver

添加好配置項後,我們在 cn.itweknow.sb-mybatis.config 包下新建了 DataSourceConfiguration 類,它是數據源的配置類,其內容如下,可以看到在這個類裏面我們配置了數據源。

清單 3. 數據源配置

@Configuration@ConditionalOnProperty(name ="spring.datasource.dbcp2.url", matchIfMissing = false)@MapperScan(value = {"cn.itweknow.sbmybatis.mapper"}, sqlSessionFactoryRef ="sqlSessionFactory")public class DataSourceConfiguration {@Bean(name ="dataSource")@ConfigurationProperties(prefix ="spring.datasource.dbcp2")public DataSource dataSource() {returnnewBasicDataSource();}}

到這一步,如果我們能夠正常啓動項目就意味着我們的連接池配置成功了。

集成 MyBatis

下面我們來了解如何在 Spring Boot 項目中配置 MyBatis。只需要三大步驟就可以完成這個配置。第一步是添加依賴包,第二步是配置數據庫事務和會話工廠,最後一步就是配置 Mapper 的路徑。

添加 MyBatis 相關依賴包

我們只需要在 pom.xml 文件的 <dependencies> 標籤下添加如下內容即可。

清單 4. 添加 MyBatis 依賴包

<!--mybatis-->org.mybatis.spring.bootmybatis-spring-boot-starter1.3.2

配置數據庫事務和會話工廠

清單 5. 配置數據庫事務和會話工廠

@Bean(name ="transactionManager")publicDataSourceTransactionManager dbOneTransactionManager(@Qualifier("dataSource")DataSource dataSource) {returnnew DataSourceTransactionManager(dataSource);}@Bean(name ="sqlSessionFactory")@ConditionalOnMissingBean(name ="sqlSessionFactory")publicSqlSessionFactory dbOneSqlSessionFactory(@Qualifier("dataSource")DataSource dataSource) throws Exception {finalSqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);returnsessionFactory.getObject();}

在 DataSourceConfiguration 類中添加上面的代碼塊,我們就配置了一個事務管理器和一個會話工廠。

配置 Mapper 路徑

首先指定 *Mapper.java 的掃描路徑(即存放 *Mapper.java 的包地址)。我們可以通過在 DataSourceConfiguration 類或者 SbMybatisApplication 類上添加 @MapperScan 註解來指定掃描路徑:

清單 6. 配置 *Mapper.java 掃描路徑

@MapperScan(value = {"cn.itweknow.sbaop.db.mapper"}, sqlSessionFactoryRef ="sqlSessionFactory")publicclassDataSourceConfiguration{…}

通過會話工廠指定指定 *Mapper.xml 的路徑。修改 SqlSessionFactory 的 Bean 創建方法如下所示:

清單 7. 配置 *Mapper.xml 掃描路徑

@Bean(name ="sqlSessionFactory")@ConditionalOnMissingBean(name ="sqlSessionFactory")publicSqlSessionFactory dbOneSqlSessionFactory(@Qualifier("dataSource")DataSource dataSource)throws Exception {finalSqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:sqlmap/*Mapper.xml"));returnsessionFactory.getObject();}

至此,我們的 MyBatis 和 Spring Boot 就整合完成了。下面的章節將介紹具體如何使用 MyBatis 了。MyBatis 一共提供了兩種使用方式,一種是基於 XML 配置 的方式,一種是基於註解的方式。我們將分別從這兩個方面來了解如何使用 MyBatis。

基於 XML 方式使用 MyBatis

準備工作

我們將通過一個具體的場景來講解如何使用 MyBatis 進行數據庫操作:定義一個學生實體和班級實體,並創建對應的數據庫表,然後使用 MyBatis 對其進行增、刪、改、查操作。學生類的全路徑爲 cn.itweknow.sbmybatis.model.dao.Student,班級類的全路徑爲 cn.itweknow.sbmybatis.model.dao.Clazz,相對應的數據庫表結構點擊這裏獲取。

創建 Mapper 類及 XML 文件

創建好對應的實體類後,我們還需要爲其創建對應的 Mapper.java 和 Mapper.xml 文件,需要注意的是這兩類文件需要放到前面配置數據源時指定的路徑下,在本例中 Mapper.java 文件是放在 cn.itweknow.sbmybatis.mapper 包下,而 Mapper.xml 則是放在 resources/sqlmap 目錄下。

創建 StudentMapper.java 文件

清單 8. StudentMapper.java 類

publicinterfaceStudentMapper{intinsert(Student student);intupdateIgnoreNullById(@Param("student") Student student);StudentselectById(@Param("id") int id);intdeleteById(@Param("id") int id);}

在 StudentMapper.java 類中我們定義了新增,更新(忽略空值),根據 id 查詢學生,根據 id 刪除學生四個方法,覆蓋了簡單的 CURD (Create、Update、Retrieve、Deletev操作,但是這四個只是方法的定義,那麼具體實現呢?與我們平常所接觸到的 interface 不一樣的是,它的實現不是一個具體的 Java 類,而是一個與之對應的 mapper.xml文件,也就是我們接下來要看到的 StudentMapper.xml 文件。StudentMapper.java 類中定義的接口會對應 StudentMapper.xml 中的一段 SQL 語句。

創建 StudentMapper.xml 文件

創建一個與 StudentMapper.java 類對應的 XML 文件,裏面定義了 StudentMapper.java 類中定義的 4 個方法的 SQL 實現,具體代碼如下所示,篇幅的原因,完整的 XML 內容可以查看本文源碼。

清單 9. StudentMapper.xml 文件

...insert into t_student(name,age,clazz_id,number) values(#{name},#{age},#{clazzId},#{number})update t_studentname = #{student.name}...where id = #{student.id}select * from t_student where id = #{id}...

其中 namespace 指定了該 XML 文件對應的 java 類。您可能還發現除了四個方法的定義外,還有一個 resultMap 的標籤,這個其實定義的是我們 sql 查詢的字段與實體類之間的映射關係。在 insert 方法中,我們使用了 useGeneratedKeys 和 keyProperty 兩個屬性,這兩個屬性的作用主要是將插入後數據的 id,賦值到傳進來的實體對象的某個字段,keyProperty 就是指定那個字段的名稱。

創建 ClazzMapper.java 文件

在 StudentMapper.java 中我們定義了簡單的增刪改查,在下面的代碼中我們定義了一個需要多表關聯才能實現的方法 ,selectWithStudentsById 方法會根據班級 id 查詢出來該班級的信息以及該班級裏的所有學生信息,我們定義了一個 ClazzExtend 類來接收查詢結果,ClazzExtend 類擴展了 Clazz,它包含了一個學生集合屬性,用來描述班級裏的所有學生。

清單 10. ClazzMapper.java 類

public interface ClazzMapper {

ClazzExtend selectWithStudentsById(int id);

}

創建 ClazzMapper.xml 文件

清單 11. ClazzMapper.xml 文件

selectc.idasid,c.nameasname,s.idassId,s.nameassName,s.ageassAge,s.clazz_idassClazzId,s.numberassNumberfromt_clazzascleftjoint_studentassons.clazz_id = c.idwherec.id =#{id}

注意 resultMap 和 StudentMapper.xml 中的 resultMap 有些不一樣,多了一個 collection 標籤,這個標籤是用來描述實體類中的集合屬性的,MyBatis 會自動將我們 left join 後產生的多條數據合成一個數組並返回,是不是很方便?

測試代碼

這裏舉例 ClazzMapper. selectWithStudentsById 方法的查詢測試,其他方法的測試代碼類似這裏就不列舉了,您可以嘗試一下自己編寫。

清單 12.測試方法代碼

@RunWith(SpringRunner.class)@SpringBootTestpublicclassXmlTest{@AutowiredprivateClazzMapper clazzMapper;@Testpublicvoid testSelectWithStudentsById() {ClazzExtend result = clazzMapper.selectWithStudentsById(1);System.out.println(JSON.toJSONString(result));}}

執行以上單元測試後,會發現在控制檯輸出的結果中既包含班級信息也包含班級下面的學生信息,控制檯輸出的內容如下所示:

圖 1. Mapper 方法測試結果

基於註解方式使用 MyBatis

雖然 MyBatis 設計之初是一個 XML 驅動的 ORM 框架,其配置信息都是基於 XML 的,但是從 MyBatis3 開始它基於強大的 Java 語言的配置 API 之上,支持使用註解來配置 SQL 以及查詢結果與實體之間的映射關係。那麼我們下面就來了解一下如何使用註解來使用 MyBatis。

注意本章節中所使用的表結構和例子與基於 XML 方式使用章節一致。話不多說,我們直接開始。Mapper.java 文件還是需要放在數據源指定的位置,我這裏就不重複創建 Mapper 文件了,基於註解配置的代碼也直接寫在上面創建的 Mapper 文件中。

新增語句

清單 13. 基於註解的插入數據代碼

@Insert("insert into t_student(name,age,clazz_id,number) values "+"(#{name},#{age},#{clazzId},#{number})")@Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id")int annoInsert(Student student);

查詢語句

清單 14. 基於註解的查詢數據代碼

@Select("select * from t_student where id = #{id}")Student annoSelectById(Integerid);

您可能會奇怪,這裏沒有定義數據庫字段和實體類之間的映射關係,MyBatis 是如何將查詢結果轉換成對象的呢?其實 MyBatis 會默認以下劃線轉駝峯的方式建立一個映射關係來轉換。到這裏您可能會問了,在基於註解的方式下如果字段名稱和實體類屬性名稱不符合這個映射規則的話又該如何呢?基於註解配置映射關係將會在多表關聯查詢小節中講解。

更新語句

清單 15. 基於註解的更新數據代碼

@UpdateProvider(type = StudentDAOProvider.class, method ="updateIgnoreNullByPrimaryKey")int annoUpdateIgnoreNullById(@Param("student") Student student);intannoUpdateIgnoreNullById(@Param("student") Student student);

您應該會發現 Update 方法和前面有些不同,我們指定了一個 type 和一個 method 屬性,這是爲什麼呢?因爲我們在更新的時候需要去判斷該字段是否爲空來決定是否更新該字段的內容,在 XML 的配置方式中我們可以通過 <if></if> 標籤實現。那麼通過註解呢?我們只能通過一個 Provider 類來動態生成 SQL 語句,下面的內容就是 StudentDAOProvider.class 的內容:

清單 16. StudentDAOProvider 的內容

publicclassStudentDAOProvider{publicString updateIgnoreNullByPrimaryKey(Map map) throwsException{Student student = (Student) map.get("student");if(student ==null|| student.getId() ==null) {thrownewException("the primaryKey can not be null.");}// 拼裝 sql 語句StringBuilder updateStrSb =newStringBuilder("update

t_student set ");StringBuilder setStrSb =newStringBuilder();if(student.getName() !=null) {setStrSb.append("name = #{student.name},");}if(student.getNumber() !=null) {setStrSb.append("number = #{student.number},");}if(student.getAge() !=null) {setStrSb.append("age = #{student.age},");}if(student.getClazzId() !=null) {setStrSb.append("clazz_id = #{student.clazzId},");}if(setStrSb.length() >0) {updateStrSb.append(setStrSb.substring(0, setStrSb.length()-1)).append(" where id = #{student.id}");}else{thrownewException("none element to update.");}returnupdateStrSb.toString();}}

刪除語句

清單 17. 基於註解的刪除數據代碼

@Delete("delete from t_student where id = #{id}")intannoDeleteById(Integerid);

多表關聯查詢

多表關聯查詢的例子和基於 XML 的例子相同,我們也是根據班級 id 查詢班級信息以及該班級下所有的學生信息。基於註解的一對多的關聯查詢方式會比較繁瑣,下面是具體的實現步驟。

首先在 StudentMapper.java 中定義一個根據班級 id 查詢學生集合的方法。

清單 18. 根據班級 id 查詢學生的代碼

@Select("select * from t_student where clazz_id = #{clazzId}")

List findByClazzId(Integer clazzId);

然後再在 ClazzMapper.java 中定義複雜查詢的方法,如下所示:

清單 19. 多表關聯查詢的代碼

@Select("select * from t_clazz where id = #{id}")@Results(id ="link", value = {@Result(column ="id", property ="id"),@Result(column ="name", property ="name"),@Result(property ="students", column ="id", javaType = List.class,many =@Many(fetchType = FetchType.EAGER, select = "cn.itweknow.sbmybatis.mapper.StudentMapper.findByClazzId"))})ClazzExtend annoSelectWithStudentsById(Integer id);

可以看到我們在基於註解的方式下可以通過 @Result 來制定查詢結果與實體之間的映射關係。你可能還注意到查詢班級中所有學生的屬性實際上是指定了 StudentMapper 中的 findByClazzId 的方法來實現的,many=@Many 是指的這是一個一對多的查詢,如果是一對一的查詢的話可以使用 one=@One。select 則指定了這個一對多查詢的具體方法,可能看到這裏您會有一個疑問:這個複雜查詢時使用的關聯表查詢還是分成了兩個查詢語句查詢來得到結果的呢?那我們就一起來看一下吧:我們修改一下日誌級別爲 debug,這樣可以將 MyBatis 實際執行的 SQL 語句打印出來,如下圖所示。

圖 2. 基於註解複雜查詢的 SQL 打印

很明顯可以看出來,是分成了兩個 SQL 來查詢的,這樣如果關聯表很多的話可能會導致查詢效率比較低,可見覆雜 SQL 的查詢還是 XML 的方式好使一些。

XML 與註解的優劣對比

上面分別介紹了基於 XML 和註解兩種方式來使用 MyBatis,那麼到底使用哪種方式好呢?其實這個也需要看實際使用場景,比如說註解適合使用在一些簡單查詢的場景,而 XML 則在複雜查詢的時候表現更佳。下面分別列舉一下使用 XML 和註解的優劣之處供大家參考。

XML 的優劣點

優點:

排版能力強,特別是複雜 SQL 的排版,看起來更加清晰明瞭。

動態 SQL,在編寫動態 SQL 的時候不得不說 XML 確實比註解要強,註解還需要單獨定義一個 Provider 類來提供生成的 SQL。

缺點:

需要將 Mapper.xml 與 Mapper.java 類準確對應,容易出錯。

查找一個接口對應的 SQL 語句不方便,還需要先找到對應的 XML 文件。

註解的優劣點

優點:

SQL 查找方便,SQL 直接放在接口上方的註解上,可以很容易找到。

缺點:

SQL 排版效果不好,複雜的 SQL 很難看明白其查詢邏輯。

對多表關聯查詢的支持度不好。

對動態 SQL 的支持不好,還需要單獨提供生成 SQL 的 Java 方法。

多表關聯查詢時,實際上是分成了多條 SQL 查詢,如果關聯比較多時可能會造成查詢的效率比較低。

MyBatis 分頁查詢

在實際的開發過程中,我們可能會經常遇到分頁查詢的場景。如果直接用 SQL 語句的話,MySQL 可以通過 Limit 實現,MSSQL 可以通過 TOP 或者 row_number() 實現。那麼 MyBatis 有沒有對分頁做支持呢?沒有。我們可以通過 PageHelper 這個插件來實現這個需求。

添加依賴

首先添加 PagerHelper 相關的依賴包,在 pom.xml 中添加如下內容即可:

清單 20. 添加 PageHelper 依賴

com.github.pagehelperpagehelper5.1.10

使用方式

首先在 Spring Boot 的配置文件中添加一些配置項:

清單 21. PageHelper 配置項

pagehelper:helperDialect:mysqlreasonable:true

雖然說不同的數據庫之間的 SQL 大體相同,但每種數據庫都會有自己的方言,比如說不同的數據實現分頁使用的關鍵字就不同,我們可以通過以配置 helperDialect 屬性來指定分頁插件使用哪種方言。Reasonable 是配置是否使用分頁合理化參數,默認值爲 false。當該參數設置爲 true 時,pageNum<=0 時會查詢第一頁,pageNum>pages(超過總數時),會查詢最後一頁。默認 false 時,直接根據參數進行查詢。

爲了能夠體現分頁插件的效果,在使用之前先往 t_student 表中插入 15 條數據,然後在 Mapper 層定義一個查詢所有學生的方法如下所示。

清單 22. 查詢所有學生

@Select("select *from t_student")List findAll();

注意爲了方便貼代碼,這個方法的實現我使用的是註解的方式,如果您使用 XML 的配置方式也是完全可以的。

然後通過如下的代碼就可以成功的實現分頁查詢學生列表了。

清單 23. 分頁插件的使用

@TestpublicvoidtestSelectPage(){//獲取第 1 頁,10 條內容,默認查詢總數 countPageHelper.startPage(1,10);//緊跟着的第一個 select 方法會被分頁Listlist= studentMapper.findAll();PageInfo page =newPageInfo(list);System.out.println(JSON.toJSONString(page));}

可以看到實現起來很方便,實際上 PageHelper 在調用 startPage 方法的時候會將分頁信息保存在 ThreadLocal 中,然後對緊跟在 startPage 之後的一個查詢語句應用分頁,PageHelper 會去修改你的查詢語句以達到分頁的效果。PageInfo 裏面包含了非常全面的分頁屬性如總數據條數、頁大小、當前頁數據等等。

圖 3. PageInfo 對象的分頁屬性

結束語

在本教程中,您瞭解瞭如何在 Spring Boot 項目中整合 MyBatis,更深一步介紹瞭如何通過 XML 和註解兩種方式使用 MyBatis 以及兩種方式的優缺點,最後還了解了如何通過 PageHelper 來實現分頁查詢。您可以在 GitHub 上找到本教程的完整實現,如果您想對本教程做補充的話歡迎發郵件([email protected])給我或者直接在 GitHub 上提交 Pull Request。

喜歡的話可以給小編點點贊哦,小編還收集各種Java多種實戰書籍!!一線大廠面題,簡歷模板,面試指南,等學習資料

《Java學習、面試;文檔、視頻資源免費獲取》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章