SpringBoot 開發實踐(3):優雅使用 SpringBoot + Druid + MyBatis 訪問數據庫

前言

作爲 Java 後端開發,我們免不了要和數據庫打交道。那麼我們如何在 SpringBoot 中優雅地與數據庫交互呢?

目前,主流的方式是使用 JPA 或者 MyBatis 作爲訪問數據庫的框架。JPA 的前身是 Hibernate,其宣傳的亮點是不需要寫 SQL 就能實現數據的交互。對於簡單的單表查詢來說,JPA 有其優勢,可以使開發效率大大提高。但是對於複雜的多表關聯查詢場景,MyBatis 靈活的優勢就凸顯出來了。

我在一開始用 SpringBoot 訪問數據庫的時候,用的就是 MyBatis,所以對 MyBatis 的使用相對熟悉一些。而自工作以來,見到的項目大部分都是 SSM 架構的。所以本章就針對 MyBatis 介紹它與 SpringBoot 如何進行整合。

配置工程環境

首先,我們來梳理下,都需要哪些框架或插件。

MyBatis

既然是 SSM,MyBatis 自然是少不了。

MyBatis 提供了 starter 支持,在 pom.xml 中的 <dependencies> 標籤中添加 mybatis-spring-boot-starter 依賴,就能輕鬆地在項目中使用 MyBatis 了。

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

Druid

Druid 是阿里巴巴開源的數據庫連接池,是目前較爲公認的最好用的連接池。它擁有強大的監控功能,在性能、擴展性方面都表現出色。目前生產中使用 Druid 作爲項目的連接池非常普遍,所以這裏介紹下如何將 Druid 整合進來。

導入 maven 依賴

同樣,在 pom.xml 中導入 druid-spring-boot-starter

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid-spring-boot-starter</artifactId>
   <version>1.1.22</version>
</dependency>

配置 application.yml

由於有了 druid-spring-boot-starter 的支持,SpringBoot 會自動到 yml 中讀取相關配置,前提是屬性名和層次要按照官方文檔的要求來配置。Yml 配置如下:

spring:
  datasource:
    name: mysql
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      url: jdbc:mysql://127.0.0.1:3306/springboot_study?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
      username: root
      password: 123456
  • name: 給你的數據源命個名。
  • type: SpringBoot 默認的數據源是 org.apache.tomcat.jdbc.pool.DataSource,這裏我們要其配置成 Druid。
  • url: 數據庫地址
  • username:數據庫用戶名
  • password:數據庫密碼

配置到這兒,Druid 就可以正常使用了,其它的配置例如連接個數、超時時間等這些目前都是默認值。當然,一般我們在生產中,會根據實際需要,手動配置其它參數。具體的各參數介紹可以查看官方文檔: DruidDataSource配置屬性列表

這裏也附上一份我們生產中配置的一個例子,供大家參考

druid:
  #監控統計攔截的filters
  filters: stat, wall,log4j
  driver-class-name: com.mysql.jdbc.Driver
  #配置初始化大小/最小/最大
  initial-size: 5
  min-idle: 5
  max-active: 20
  #獲取連接等待超時時間
  max-wait: 60000
  #間隔多久進行一次檢測,檢測需要關閉的空閒連接
  time-between-eviction-runs-millis: 60000
  #一個連接在池中最小生存的時間
  min-evictable-idle-time-millis: 300000
  validation-query: SELECT 'x'
  test-while-idle: true
  test-on-borrow: false
  test-on-return: false
  #打開PSCache,並指定每個連接上PSCache的大小。oracle設爲true,mysql設爲false。分庫分表較多推薦設置爲false
  pool-prepared-statements: false
  max-pool-prepared-statement-per-connection-size: 20

有關更多 Druid Starter 的配置方式,可以參考官方文檔:druid-spring-boot-starter

MySQL 驅動

由於我們連接的是 MySQL 數據庫,所以需要引入 MySQL 驅動。

pom.xml 中導入 mysql-connector-java

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>8.0.20</version>
</dependency>

至此,有關 SpringBoot + Druid + MyBatis 的工程配置都已經結束,接下來就可以專注於 MyBatis 的使用了。

MyBatis 的使用

方法一:使用 XML 編寫 SQL

這種方法是 MyBatis 的傳統用法,通過編寫一個 Mapper 類和一個 XML 文件,其中 XML 中負責編寫各種 SQL,然後將 XML 映射到 Mapper 上,每個 SQL 與 Mapper 中的方法一一對應。這樣通過調用 Mapper 中方法就可以與數據庫交互了。

創建實體類

創建一個 entity 包,生成一個 People.java 類,用於存放表中各列的值。

public class People {
    /**
     * 主鍵
     */
    private int id;
    
    /**
     * 姓名
     */
    private String name;
    
    /**
     * 年齡
     */
    private int age;

    public People() {
    }

    public People(String name, int age) {
        this.age = age;
        this.name = name;
    }

    public People(int id, String name, int age) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

編寫 Mapper

新建一個 mapper 包,生成一個 PeopleXmlMapper.java 接口類。

@Mapper
public interface PeopleXmlMapper {
    /**
     * 根據 id 查詢
     *
     * @param id
     * @return
     */
    People selectById(@Param("id") int id);

    /**
     * 插入數據
     *
     * @param people
     * @return
     */
    int insert(People people);

    /**
     * 更新數據
     *
     * @param people
     * @return
     */
    int updateById(People people);

    /**
     * 根據 id 刪除數據
     *
     * @param id
     * @return
     */
    int deleteById(@Param("id") Integer id);
}

在這個接口中,我們定義四個方法,分別對應增刪改查四種操作。

  • @Mapper: 用於表示該接口類是個 Mapper, 在編譯後會生成相應的實現類。
  • @Param: 用於給參數命名,動態替換 SQL 中的參數值。
  • People: MyBatis 支持直接傳入和返回對象實體,在 XML 中配置好實體屬性和列的關係,就可以在 SQL 中取到實體中的屬性值,或者將查得的數據以實體形式返回。

編寫 SQL XML

resources 目錄下,新建一個 mappers 文件夾,然後在裏面新建一個 PeopleMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.interhorse.springboot.mybatisdruidmysql.dao.mapper.PeopleXmlMapper">
    <resultMap id="BaseResultMap" type="cn.interhorse.springboot.mybatisdruidmysql.entity.People">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="age" jdbcType="INTEGER" property="age"/>
    </resultMap>

    <select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select id, name, age
        from people
        where id = #{id}
    </select>

    <insert id="insert" parameterType="cn.interhorse.springboot.mybatisdruidmysql.entity.People">
        insert into people(name, age) values(#{name}, #{age})
    </insert>


    <update id="updateById" parameterType="cn.interhorse.springboot.mybatisdruidmysql.entity.People">
        update people set name = #{name}, age = #{age} where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Integer">
        delete from people where id = #{id}
    </delete>
</mapper>

一個 XML 與一個 Mapper 類一一對應。在 Mapper 類中的接口的具體實現,都將在 XML 中進行。每條 SQL 的 id 要和 Mapper 中的方法名做對應,#{} 中的值會被替換成入參值。

  • namespace: 將該 XML 與 Mapper 綁定。
  • resultMap: 將實體類中的屬性和表中的列做好映射關係。
  • parameterType: 入參類型。

更詳細的 MyBatis XML 映射器介紹,請參閱: sqlmap-xml

其它配置

application.yml 中,添加如下配置:

# MyBatis
mybatis:
  mapper-locations: classpath:mappers/*.xml

這是爲了讓 SpringBoot 知道 XML 文件的所在位置。

在之前編寫的 Mapper 類中,我們使用 @Mapper 註解來進行標註。如果 Mapper 數量特別多的情況下,每個 Mapper 類都要打上 @Mapper 註解就很不方便了。所以我們可以在 Application 類中,配置 @MapperScan,對 Mapper 統一掃描,這樣就不需要每個都打上 @Mapper 註解了。

@SpringBootApplication
@MapperScan("cn.interhorse.springboot.mybatisdruidmysql.dao.mapper")
public class MybatisDruidMysqlApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisDruidMysqlApplication.class, args);
    }
}
  • @MapperScan: 配置 Mapper 接口類所在的包,該包下所有接口在編譯後都會生成相應的實現類。不配的話,SpringBoot 在啓動時便無法找到這些 Mapper。

使用

至此,使用 XML 編寫 SQL 的基本工作都已完成。我們可以在 MyController 中編寫幾個接口,來測試下增刪改查。

@Controller
public class MyController {
    @Resource
    private PeopleXmlMapper peopleXmlMapper;
    
    /**
     * 根據 id 查詢 - xml 方式
     * @param id
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/xml/selectById/{id}", method = RequestMethod.GET)
    private Object selectByIdXml(@PathVariable("id") int id) {
        return peopleXmlMapper.selectById(id);
    }

    /**
     * 插入數據 - xml 方式
     * @param httpServletRequest
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/xml/insert", method = RequestMethod.GET)
    private Object insertXml(HttpServletRequest httpServletRequest) {
        String name = httpServletRequest.getParameter("name");
        int age = Integer.parseInt(httpServletRequest.getParameter("age"));
        People people = new People(name, age);
        return peopleXmlMapper.insert(people);
    }

    /**
     * 更新數據 - xml 方式
     * @param httpServletRequest
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/xml/updateById", method = RequestMethod.GET)
    private Object updateByIdXml(HttpServletRequest httpServletRequest) {
        String name = httpServletRequest.getParameter("name");
        int id = Integer.parseInt(httpServletRequest.getParameter("id"));
        int age = Integer.parseInt(httpServletRequest.getParameter("age"));
        People people = new People(id, name, age);
        return peopleXmlMapper.updateById(people);
    }

    /**
     * 根據 id 刪除 - xml 方式
     * @param id
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/xml/deleteById/{id}", method = RequestMethod.GET)
    private Object deleteByIdXml(@PathVariable("id") int id) {
        return peopleXmlMapper.deleteById(id);
    }
}
  • @Resource: 注入 PeopleXmlMapper。

分別訪問這幾個接口,就可以看到數據的變化了。

http://127.0.0.1:8080/xml/insert?name=Jenny&age=28

http://127.0.0.1:8080/xml/selectById/1

+----+-------+------+
| id | name  | age  |
+----+-------+------+
|  1 | Jenny |   28 |
+----+-------+------+

http://127.0.0.1:8080/xml/updateById?id=1&name=Jack&age=32

+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | Jack |   32 |
+----+------+------+

http://127.0.0.1:8080/deleteById/1

mysql> select * from people;
Empty set (0.00 sec)

方法二:使用註解編寫 SQL

SpringBoot 官方不建議使用 XML 進行配置,畢竟 SpringBoot 的設計初衷就是想簡化開發過程,給項目減負。所以得益於強大的 Java 註解(Java Annotation),MyBatis3 提供了用註解的方式映射 SQL 語句,從而告別繁瑣的 XML 配置。

創建 Mapper

mapper 包 下創建 PeopleMapper.java

public interface PeopleMapper {
    /**
     * 根據 id 查詢
     *
     * @param id
     * @return
     */
    @Select("select * from people where id = #{id}")
    People selectById(@Param("id") int id);

    /**
     * 插入數據
     *
     * @param people
     * @return
     */
    @Insert("insert into people(name, age) values(#{name}, #{age})")
    int insert(People people);

    /**
     * 更新數據
     *
     * @param people
     * @return
     */
    @Update("update people set name = #{name}, age = #{age} where id = #{id}")
    int updateById(People people);

    /**
     * 根據 id 刪除
     *
     * @param id
     * @return
     */
    @Delete("delete from people where id = #{id}")
    int deleteById(@Param("id") Integer id);
}

可以看到,在 PeopleMapper 中,同 PeopleXmlMapper 一樣,有增刪改查四種接口,但這次,SQL 語句直接配置在註解中。

更多有關 MyBatis 的註解介紹,請參閱: mybatis-java-api

其它配置

方法一中,在 application.yml 配置了 mapper-locations,而現在,因爲不需要編寫 XML,所以不需要配置該項了。

使用

使用方式和方法一一樣,通過注入對應的 Mapper,調用其中的方法即可。這裏我就不貼詳細測試代碼了,具體代碼已經上傳至 GitHub。

SQL 日誌輸出

工程默認是不輸出 SQL 日誌的,可通過配置將 SQL 日誌打印出來,方便調試。

application.yml 中添加如下配置

logging:
  level:
    cn:
      interhorse:
        springboot:
          mybatisdruidmysql:
            dao:
              mapper: debug

這樣在 console 中,就能看到 SQL 語句了。

2020-06-05 18:04:30.534 DEBUG 5392 --- [nio-8080-exec-1] c.i.s.m.d.m.PeopleXmlMapper.selectById   : ==>  Preparing: select id, name, age from people where id = ? 
2020-06-05 18:04:30.586 DEBUG 5392 --- [nio-8080-exec-1] c.i.s.m.d.m.PeopleXmlMapper.selectById   : ==> Parameters: 1(Integer)
2020-06-05 18:04:30.618 DEBUG 5392 --- [nio-8080-exec-1] c.i.s.m.d.m.PeopleXmlMapper.selectById   : <==      Total: 0

總結

以上就是 SpringBoot + Druid + MyBatis 的整合。

對於兩種使用方式,我目前青睞於第一種。主要也是因爲一直以來都使用 XML 方式編寫 SQL,不過通過本次整理 SpringBoot 系列,也發現了註解開發的優勢,即簡便、清爽。但需要注意的是,註解方式還沒有完全覆蓋 XML 所提供的功能,例如註解形式不支持抽取可複用的 SQL 語句。所以在實際開發中,要做好取捨,選擇適合自己的開發方式。

本章代碼地址:GItHub


我是因特馬,一個愛分享的斜槓程序員~

歡迎關注我的公衆號:一隻因特馬

原文作者: 一隻因特馬
原文鏈接: https://www.matalking.com/a/3500956215/
版權聲明: 本博客所有文章除特別聲明外,均採用 BY-NC-ND 許可協議。轉載請註明出處!

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