Spring工程中 mysql 數據庫的使用

一、引言

數據庫相關的組件封裝和屏蔽了業務代碼對mysql數據庫訪問的諸多細節,有必要對這些細節進行深入的瞭解,構建完整的數據庫訪問的知識鏈路。

二、知識點

2.1 JDBC

參考:https://blog.csdn.net/luanlouis/article/details/30060755

1、JDBC:Java Database Connection,表示數據庫連接(任何數據庫都支持JDBC的連接),是一個獨立於特定數據庫管理系統、通用的sql數據庫存取和操作的公共接口。它是java中專門提供的一組用於操作數據庫的標準,所有的數據庫生產商如果要是想爲java提供支持,則必須支持此標準。所以說JDBC實際上是一套訪問數據庫的接口

2、java定義JDBC接口,數據庫來實現這個接口。不同的廠商對JDBC有不同的實現,所以廠商要提供自己數據庫的驅動,讓應用程序來訪問。而應用程序則只通過自己的一套JDBC接口來訪問就行了,然後驅動程序來實現這個接口,從而讓驅動來調用數據庫。因此驅動程序由數據庫提供商提供下載

觀察數據庫的配置文件,可以發現數據庫驅動是通過driver-class-name 指定的。

spring:
  datasource:
    url: jdbc:mysql://**:**/**?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true
    username: **
    password: **
    driver-class-name: com.mysql.jdbc.Driver
    initial-size: 10   // 初始的連接池大小
    validation-query: SELECT 1 
    max-idle: 8 //最大的空閒連接,空閒線程超過此數量,會被關閉
    min-idle: 5 //最小的空閒連接,當新的連接請求到達,消耗了一個空閒連接,而空閒連接小於此數量,但是總的線程數沒有超過上限,則會創建新的連接
    test-on-borrow: true // 必須設置

3、常用操作類/接口

DriverManager類、Connection接口、Statement接口、PreapredStatement接口、ResultSet接口、CallableStatement接口。

4、JDBC應用步驟

  • 註冊加載一個驅動
  • 創建數據庫連接(Connection)
  • 構造SQL語句
  • 創建statement,發送sql語句
  • 執行sql語句
  • 處理sql結果
  • 關閉statement和connection 

5、使用JDBC訪問數據庫的原始程序

package com.vae.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class JdbcTest {

    /**
     * @param args
     */
    
    //數據庫連接地址
    private final static String URL = "jdbc:mysql://localhost:3306/mydb_2";
    //用戶名
    public final static String USERNAME = "root";
    //密碼
    public final static String PASSWORD = "8888";
    //加載的驅動程序類(這個類在導入的數據庫驅動jar包中)
    public final static String DRIVER = "com.mysql.jdbc.Driver";
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        insert();
    }    
    
    
    //方法:插入數據
    public static void insert(){
        try {
            //1、加載數據庫驅動程序,Class.forName返回的是Class對象引用,這裏並未接收對應引用,本質是利用Class.forName的“副作用”:
            //如果指定類未被加載則加載該類(加載過程中會執行static子句,?static變量呢)
            Class.forName(DRIVER);
            
            //2、獲取數據庫連接
            Connection connection  = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            
            //3、構造SQL語句(插入具體的數據)
            String sql = "insert into person(name,age,description)values('程序',27,'數據庫測試')";
            
            //4、構造一個Statement實例(用來發送SQL語句的載體)
            Statement state = connection.createStatement();
            
            //5、執行SQL語句(其實是向數據庫中發送sql語句)
            state.executeUpdate(sql);
            
            //6、關閉連接(釋放資源)
            state.close();
            connection.close();
            
            System.out.println("insert success");
            
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
    }    
 
}

2.2 mysql數據庫

1、數據庫和數據庫實例

數據庫:物理操作系統文件或其他形式文件類型的集合。(認爲是物理存儲文件)

實例:運行的數據庫進程。(運行的程序)

2、mysql數據庫體系結構

 

MySQL向外提供的交互接口(Connectors)Connectors組件,是MySQL向外提供的交互組件,如java,.net,php等語言可以通過該組件來操作SQL語句,實現與SQL的交互。

管理服務組件和工具組件(Management Service & Utilities) 提供對MySQL的集成管理,如備份(Backup),恢復(Recovery),安全管理(Security)等。具體:

  • 數據庫備份和恢復
  • 數據庫安全管理,如用戶及權限管理
  • 數據庫複製管理
  • 數據庫集羣管理
  • 數據庫分區,分庫,分表管理
  • 數據庫元數據管理

連接池組件(Connection Pool) 負責監聽對客戶端向MySQL Server端的各種請求,接收請求,轉發請求到目標模塊。每個成功連接MySQL Server的客戶請求都會被創建或分配一個線程,該線程負責客戶端與MySQL Server端的通信,接收客戶端發送的命令,傳遞服務端的結果信息等。

SQL接口組件(SQL Interface) 接收用戶SQL命令,如DML,DDL和存儲過程等,並將最終結果返回給用戶。

查詢分析器組件(Parser)  解析器的作用主要是解析查詢語句,最終生成語法樹。首先解析器會對查詢語句進行語法分析,如果語句語法有錯誤,則返回相應的錯誤信息。語法檢查通過後,解析器會查詢緩存,如果緩存中有對應的語句,就直接返回結果不進行接下來的優化執行操作。(注:緩存中數據被修改,會被清出緩存,因此不存在緩存和實際數據不一致的情況。)

優化器組件(Optimizer)優化器的作用主要是對查詢語句進行優化,包括選擇合適的索引,數據的讀取方式。

緩存主件(Caches & Buffers)包括全局和引擎特定的緩存,提高查詢的效率。如果查詢緩存中有命中的查詢結果,則查詢語句就可以從緩存中取數據,無須再通過解析和執行。這個緩存機制是由一系列小緩存組成,如表緩存、記錄緩存、key緩存、權限緩存等。

MySQL存儲引擎 MySQL屬於關係型數據庫,而關係型數據庫的存儲是以表的形式進行的,對於表的創建,數據的存儲,檢索,更新等都是由MySQL存儲引擎完成的,這也是MySQL存儲引擎在MySQL中扮演的重要角色。MySQL的存儲引擎是插件式的,可根據需要選擇相應的存儲引擎。存儲引擎是基於表的,而非數據庫

 

2.4 Spring 數據庫連接池

1、這裏描述的數據庫連接池是應用程序的連接池,而非是數據庫實例的連接池。

2、Spring boot 2.0  之後默認數據庫連接池是HikariCP,在此之前默認是Tomcat。如果想指定數據庫連接池,配置spring.datasource.type。所有的配置項要在對應的數據庫連接池下面,否則是無效的。tomcat默認數據庫連接池配置參考:https://wiki.jikexueyuan.com/project/tomcat/tomcat-jdbc-pool.html

3、關鍵參數設置

備註:MySQL的默認情況下,當一個連接的空閒時間超過8小時後,MySql會斷開該連接,而客戶端代碼的連接池則以爲該連接依然有效,這種情況下,如何客戶端代碼向連接池獲取連接,連接池就會把失效的連接返回給客戶端代碼,客戶端代碼在使用該失效的連接後就會拋出上面的異常。

因此必須要有相應的措施,解決MySQL主動失效連接的問題。思路主要有3種(這裏討論的是Tomcat內置數據庫連接池):

  • 更改數據庫設置,避免或是延長MySQL連接失效。(不太好)
  • 當從連接池取出連接時,先測試其可用性。(依賴兩個配置項:test-on-borrow開關、validation-query檢查連接有效性的語句)
  • 定時檢查連接池中空閒的連接,如果失效則將其清除。(依賴3個配置項:test-while-idle開關、validation-query、timeBetweenEvictionRunsMillis 單位是毫秒,不能低於1s,其決定了檢查連接池中空閒的連接是否有效的頻率,默認爲 5000

test-on-borrow對性能影響較大,默認是關閉的,而且也不推薦開啓。

spring:
  datasource:
    url: jdbc:mysql://**:**/**?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true
    username: **
    password: **
    driver-class-name: com.mysql.jdbc.Driver
    initial-size: 10   // 無效配置
    validation-query: SELECT 1 
    max-idle: 8 //無效配置
    min-idle: 5 //無效配置
    tomcat:
      testWhileIdle: true
      validationQuery: SELECT 1
      timeBetweenEvictionRunsMillis: 40000
      initial-size: 10   // 初始的連接池大小
      max-idle: 8 //最大的空閒連接,空閒線程超過此數量,會被關閉
      min-idle: 5 //最小的空閒連接,當新的連接請求到達,消耗了一個空閒連接,而空閒連接小於此數量,但是總的線程數沒有超過上限,則會創建新的連接
      testOnBorrow: true

數據庫連接數會在  initial-size - minIdle - maxIdle - maxActive之間變化。變化的邏輯描述如下:

  • 初始時是initial-size,隨着應用併發訪問數據庫的增多,連接數也增多,但都與minIdle值無關,很快minIdle被超越,minIdle值一點用都沒有。直到連接的數量達到maxIdle值,這時的連接都是隻增不減的。 再繼續發展,連接數再增多並超過maxIdle時,使用完的連接(剛剛空閒下來的)會立即關閉,總體連接的數量穩定在maxIdle但不會超過maxIdle。但活動連接(在使用中的連接)可能數量上瞬間超過maxIdle,但永遠不會超過maxActive。

  • 如果開啓了testWhileIdle,會檢查空閒連接的可用性,如果無效將被清除。該功能會使數據庫連接數減少,如果小於minIdle,則會創建新的連接(猜測是在清除無效連接時執行的邏輯)。頻率是timeBetweenEvictionRunsMillis控制的。

  • minEvictableIdleTimeMillis 是指連接在池中保持空閒狀態的最長時間(以毫秒計),超過的話,會被清除。(邏輯觸發的時機不清楚,可能跟testWhileIdle是一起執行的。這些細節,對於不同的連接池實現都是不一樣的。)

 

2.5 JPA、Spring Data JPA、Hibernate和Mybatis

2.5.1 JPA

Java Persistence API(Java 持久層 API):用於對象持久化的 API,使得應用程序以統一的方式訪問持久層。它是一種規範。

2.5.2 Spring Data JPA

Spring Data JPA是Spring Data的子模塊,可以理解爲 是JPA 規範的再次封裝抽象。Spring Data JPA默認使用Hibernate作爲底層實現,所以,一般使用Spring Data JPA即會使用Hibernate。可以認爲Spring Data JPA是JPA的實現。

2.5.3 Hibernate

Hibernate是一種Java語言下的對象關係映射(Object Relational Mapping,簡稱ORM)解決方案。它爲面向對象的領域模型到傳統的關係型數據庫的映射,提供了一個使用方便的持久化框架。

2.5.4 Mybatis

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

2.5.5 Hibernate和Mybatis對比

Spring Data JPA底層調用的是Hibernate,因此就持久層框架這一塊,僅比較Hibernate和Mybatis。

Hibernate 是完備的 ORM 框架,是符合 JPA 規範的,但 MyBatis 不是。MyBatis 比單純寫 JDBC 肯定是方便一點,但無可避免還是要寫SQL,且無法做到跨數據庫 。Hibernate 使用 JPA 就可以無需考慮數據庫的兼容性問題。

Mybatis是半自動的框架。之所以稱它爲半自動,是因爲它需要手工匹配提供POJO,sql和映射關係,而全表映射的Hibernate只需要提供POJO和映射關係即可。

Hibernate作爲留下的Java orm框架,它確實編程簡易,需要我們提供映射的規則,完全可以通過IDE生成,同時無需編寫sql確實開發效率優於Mybatis。此外Hibernate還提供了緩存,日誌,級聯等強大的功能,但是Hibernate的缺陷也是十分明顯,多表關聯複雜sql,數據系統權限限制,根據條件變化的sql,存儲過程(創建自定義的查詢函數,並寫到數據庫中,以供後續使用)等場景使用Hibernate十分不方便,而性能又難以通過sql優化,所以註定了Hibernate只適用於在場景不太複雜,要求性能不太苛刻的時候使用。

如果你需要一個靈活的,可以動態生成映射關係的框架,那麼Mybatis確實是一個最好的選擇。它幾乎可以替代jdbc,擁有動態列,動態表名,存儲過程支持,同時提供了簡易的緩存,日誌,級聯。但是它的缺陷是需要你提供映射規則和sql,所以開發工作量比hibernate要大些。

總結:Hibernate 複雜,效率低、靈活性差,但是使用簡單;Mybatis簡單、效率高,但是使用複雜。

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