1.JDBC介紹
1.1 1.1 JDBC介紹
2.JDBC之API
2.1 2.1 JDBC之API
3.JDBC例子
3.1 3.1 前期準(zhǔn)備DBUtil
3.2 3.2 DBUtil實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的CURD
4.JDBC之事務(wù)管理
4.1 4.1 事務(wù)基礎(chǔ)概念
4.2 4.2 事務(wù)實(shí)現(xiàn)轉(zhuǎn)賬功能
5.數(shù)據(jù)庫連接池
5.1 數(shù)據(jù)庫連接池介紹
5.2 JNDI訪問數(shù)據(jù)源
5.3 開源數(shù)據(jù)庫連接池
一违崇、JDBC開發(fā)步驟
二坯沪、JDBC事務(wù)管理
Connection.setAutoCommit(boolean autoCommit) 修改自動(dòng)提交模式(默認(rèn)為true)
Connection.commit() 提交事務(wù)
Connection.rollback() 事務(wù)回滾
1榔袋、什么是事務(wù)
事務(wù)(Transaction):是并發(fā)控制的單元,是用戶定義的一個(gè)操作序列结洼。這些操作要么都做,要么都不做岛抄,是一個(gè)不可分割的工作單位璃诀。通過事務(wù),sql server 能將邏輯相關(guān)的一組操作綁定在一起谷暮,以便服務(wù)器 保持?jǐn)?shù)據(jù)的完整性蒿往。事務(wù)通常是以begin transaction開始,以commit或rollback結(jié)束湿弦。Commint表示提交瓤漏,即提交事務(wù)的所有操作。具體地說就是將事務(wù)中所有對(duì)數(shù)據(jù)的更新寫回到磁盤上的物理數(shù)據(jù)庫中去颊埃,事務(wù)正常結(jié)束蔬充。Rollback表示回滾,即在事務(wù)運(yùn)行的過程中發(fā)生了某種故障竟秫,事務(wù)不能繼續(xù)進(jìn)行娃惯,系統(tǒng)將事務(wù)中對(duì)數(shù)據(jù)庫的所有已完成的操作全部撤消,滾回到事務(wù)開始的狀態(tài)肥败。
事務(wù)的特性:
- 原子性(atomicity):事務(wù)是數(shù)據(jù)庫的邏輯工作單位趾浅,而且是必須是原子工作單位,對(duì)于其數(shù)據(jù)修改馒稍,要么全部執(zhí)行皿哨,要么全部不執(zhí)行。
- 一致性(consistency):事務(wù)在完成時(shí)纽谒,必須是所有的數(shù)據(jù)都保持一致狀態(tài)证膨。在相關(guān)數(shù)據(jù)庫中,所有規(guī)則都必須應(yīng)用于事務(wù)的修改鼓黔,以保持所有數(shù)據(jù)的完整性央勒。
- 隔離性(isolation):一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)所影響。
- 持久性(durability):一個(gè)事務(wù)一旦提交澳化,事物的操作便永久性的保存在DB中崔步。即使此時(shí)再執(zhí)行回滾操作也不能撤消所做的更改。
2缎谷、Java JDBC事務(wù)機(jī)制
JDBC對(duì)事務(wù)的支持體現(xiàn)在三個(gè)方面:
自動(dòng)提交模式(Auto-commit mode)
Connection提供了一個(gè)auto-commit的屬性來指定事務(wù)何時(shí)結(jié)束井濒。
- a. 當(dāng)auto-commit為true時(shí)(默認(rèn)為true),當(dāng)每個(gè)獨(dú)立SQL操作的執(zhí)行完畢,事務(wù)立即自動(dòng)提交瑞你,也就是說每個(gè)SQL操作都是一個(gè)事務(wù)酪惭。一個(gè)獨(dú)立SQL操作什么時(shí)候算執(zhí)行完畢,JDBC規(guī)范是這樣規(guī)定的:
對(duì)數(shù)據(jù)操作語言(DML者甲,如insert,update,delete)和數(shù)據(jù)定義語言(如create,drop)春感,語句一執(zhí)行完就視為執(zhí)行完畢。
對(duì)select語句虏缸,當(dāng)與它關(guān)聯(lián)的ResultSet對(duì)象關(guān)閉時(shí)甥厦,視為執(zhí)行完畢。
對(duì)存儲(chǔ)過程或其他返回多個(gè)結(jié)果的語句寇钉,當(dāng)與它關(guān)聯(lián)的所有ResultSet對(duì)象全部關(guān)閉,所有update count(update,delete等語句操作影響的行數(shù))和output parameter(存儲(chǔ)過程的輸出參數(shù))都已經(jīng)獲取之后舶赔,視為執(zhí)行完畢岭粤。
- b. 當(dāng)auto-commit為false時(shí)拒担,每個(gè)事務(wù)都必須顯示調(diào)用commit方法進(jìn)行提交,或者顯示調(diào)用rollback方法進(jìn)行回滾。auto-commit默認(rèn)為true形病。
例子
try {
conn.setAutoCommit(false); //將自動(dòng)提交設(shè)置為false
ps.executeUpdate("修改SQL"); //執(zhí)行修改操作
ps.executeQuery("查詢SQL"); //執(zhí)行查詢操作
conn.commit(); //當(dāng)兩個(gè)操作成功后手動(dòng)提交
} catch (Exception e) {
conn.rollback(); //一旦其中一個(gè)操作出錯(cuò)都將回滾,使兩個(gè)操作都不成功
e.printStackTrace();
}
三侍匙、數(shù)據(jù)庫連接池
數(shù)據(jù)庫連接池的實(shí)現(xiàn)方法:
- JNDI
- 開源框架:DBCP迈喉、C3P0等
- 公司用:持久層框架Mybatis
從數(shù)據(jù)庫連接池獲取的Connection是不需要關(guān)閉的,連接池將根據(jù)情況自動(dòng)關(guān)閉桶略。
1语淘、使用JNDI配置數(shù)據(jù)庫連接池
參考:JNDI 是什么 | Tomcat配置JNDI
JNDI(Java Naming and Directory Inteface)即Java名稱目錄接口。JNDI的作用:就是將資源引入到服務(wù)器中际歼』谭可以將JNDI當(dāng)成一個(gè)倉庫。將Java對(duì)象放入到JNDI中去鹅心。
JNDI提供了一種服務(wù)吕粗,這個(gè)服務(wù)可以將“名稱”和“資源”進(jìn)行綁定,然后程序員通過面向JNDI接口調(diào)用方法lookup可以查找到相關(guān)的資源旭愧。
基于JNDI的特性颅筋,用了JNDI之后建立數(shù)據(jù)庫連接池的做法:首先,在J2EE容器中配置JNDI參數(shù)输枯,定義一個(gè)數(shù)據(jù)源议泵,也就是JDBC引用參數(shù),給這個(gè)數(shù)據(jù)源設(shè)置一個(gè)名稱用押;然后肢簿,在程序中,通過數(shù)據(jù)源名稱引用數(shù)據(jù)源從而訪問后臺(tái)數(shù)據(jù)庫。
2池充、使用開源框架:以DBCP為例
DBCP(DatabaseConnection Pool)是一個(gè)依賴Jakarta commons-pool對(duì)象池機(jī)制的數(shù)據(jù)庫連接池桩引,Tomcat的數(shù)據(jù)源使用的就是DBCP。
DBCP使用Demo
1收夸、引入依賴
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
2坑匠、配置文件(.properties)
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/db_qunar_fresh2017
jdbc.username=root
jdbc.password=root
#初始化連接
dataSource.initialSize=10
#最大空閑連接
dataSource.maxIdle=20
#最小空閑連接
dataSource.minIdle=5
#最大連接數(shù)量
dataSource.maxActive=50
#是否在自動(dòng)回收超時(shí)連接的時(shí)候打印連接的超時(shí)錯(cuò)誤
dataSource.logAbandoned=true
#是否自動(dòng)回收超時(shí)連接
dataSource.removeAbandoned=true
#超時(shí)時(shí)間(以秒數(shù)為單位)
dataSource.removeAbandonedTimeout=180
#超時(shí)等待時(shí)間以毫秒為單位 6000毫秒/1000等于60秒
dataSource.maxWait=1000
3、創(chuàng)建數(shù)據(jù)庫連接池卧惜、獲取連接(封裝工具類)
package com.qunar.fresh2017.db;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* Copyright(C),2005-2017,Qunar.com
* version date author
* ──────────────────────────────────
* 1.0 17-3-9 wanlong.ma
* Description:DBCP連接池工具類
* Others:
* Function List:
* History:
*/
public class DataSourceUtil {
private static Logger logger = LoggerFactory.getLogger(DataSourceUtil.class);
private static Properties properties = new Properties();
private static DataSource dataSource;
static {
// 讀取配置文件
try {
properties.load(DataSourceUtil.class.getResourceAsStream("/dbcp.properties"));
} catch (IOException e) {
logger.error("讀取數(shù)據(jù)庫連接池配置文件失斃遄啤:/dbcp.properties",e);
}
// 創(chuàng)建數(shù)據(jù)庫連接池
try {
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
logger.error("數(shù)據(jù)庫連接池創(chuàng)建失敗",e);
}
}
/**
* 獲取連接
* @return
*/
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
logger.error("從數(shù)據(jù)庫連接池中獲取連接失敗",e);
}
return connection;
}
}
3、使用數(shù)據(jù)庫連接池是否需要關(guān)閉Connection
我們?cè)谄綍r(shí)項(xiàng)目中用到了數(shù)據(jù)庫連接池咽瓷,比如C3P0,DBCP,JNDI...
在使用結(jié)束的時(shí)候我們也要關(guān)閉連接设凹。
為什么呢。具體解釋如下:使用 C3P0 的話茅姜,也是 java.sql.Connection闪朱,只要是 JDBC 都是這個(gè)接口的對(duì)象!
使用完后必須 con.close() 掉 钻洒,使用連接池的話奋姿,執(zhí)行 con.close 并不會(huì)關(guān)閉與數(shù)據(jù)庫的 TCP 連接,而是將連接還回到池中去素标,如果不 close 掉的話称诗,這個(gè)連接將會(huì)一直被占用,直接連接池中的連接耗盡為止头遭。
也就是說:我們通過數(shù)據(jù)庫連接池獲取到的Connection是經(jīng)過連接池重寫后的Connection寓免,同為 java.sql.Connection,只是在關(guān)閉連接時(shí)计维,它重寫了colse方法再榄,使得Connection并不是斷開TCP連接,而是釋放享潜、還給連接池繼續(xù)分配困鸥。
至于是如何做到 con.close 并不是真正意義上的關(guān)閉連接?而是直接將連接還回到池中去剑按? 一般有兩種方式:
-
(1)使用裝飾器模式疾就,在裝飾器構(gòu)造中傳入一個(gè)真正的 Connection,這個(gè)裝飾器實(shí)現(xiàn) Connection艺蝴,使用構(gòu)造 傳入 Connection 委托重寫所有方法猬腰,并改寫 close 方法:
public class ConnectionDecorator implements Connection { private Connection con = null; public ConnectionDecorator(Connection con) { this.con = con; } public void close() throws SQLException { // 重寫! } public void commit() throws SQLException { this.con.commit(); } public Statement createStatement() throws SQLException { return this.con.createStatement(); } public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { return this.con.createStatement(resultSetType, resultSetConcurrency); } ...... }
然后經(jīng)過連接池控制的 Connection 對(duì)象都使用該裝飾器進(jìn)行包裝
- (2):動(dòng)態(tài)代理: 使用動(dòng)態(tài)代理重新實(shí)現(xiàn) close 方法猜敢,每個(gè)獲得 Connection 是一個(gè)代理后的對(duì)象姑荷。
一個(gè)完善的連接池盒延,其架構(gòu)設(shè)計(jì)非常復(fù)雜,Connection#close 問題就是連接池諸多設(shè)計(jì)難點(diǎn)當(dāng)中的一個(gè)鼠冕。
四添寺、補(bǔ)充問題
1、JDBC批處理
當(dāng)需要向數(shù)據(jù)庫發(fā)送一批SQL語句執(zhí)行時(shí)懈费,應(yīng)避免向數(shù)據(jù)庫一條條的發(fā)送執(zhí)行计露,而應(yīng)采用JDBC的批處理機(jī)制,以提升執(zhí)行效率憎乙。
2票罐、線程并發(fā)更新問題
例如:A、B同時(shí)給C轉(zhuǎn)賬(尤其是跨數(shù)據(jù)庫情況)泞边,如何保證各方的錢是正確的该押?