一、引言
數據庫相關的組件封裝和屏蔽了業務代碼對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簡單、效率高,但是使用複雜。