JDBC介紹
- JDBC(
Java Database Connectivity
)是一個(gè)獨(dú)立于特定數(shù)據(jù)庫管理系統(tǒng)脐雪、通用的SQL數(shù)據(jù)庫存取和操作的公共接口(一組API)厌小,定義了用來訪問數(shù)據(jù)庫的標(biāo)準(zhǔn)Java類庫,(java.sql,javax.sql
)使用這些類庫可以以一種標(biāo)準(zhǔn)的方法战秋、方便地訪問數(shù)據(jù)庫資源璧亚。 - JDBC為訪問不同的數(shù)據(jù)庫提供了一種統(tǒng)一的途徑,為開發(fā)者屏蔽了一些細(xì)節(jié)問題脂信。
- JDBC的目標(biāo)是使Java程序員使用JDBC可以連接任何提供了JDBC驅(qū)動(dòng)程序的數(shù)據(jù)庫系統(tǒng)癣蟋,這樣就使得程序員無需對特定的數(shù)據(jù)庫系統(tǒng)的特點(diǎn)有過多的了解,從而大大簡化和加快了開發(fā)過程狰闪。
JDBC體系結(jié)構(gòu)
JDBC接口(API)包括兩個(gè)層次:
- 面向應(yīng)用的API:
Java API
疯搅,抽象接口,供應(yīng)用程序開發(fā)人員使用(連接數(shù)據(jù)庫埋泵,執(zhí)行SQL語句幔欧,獲得結(jié)果)。 - 面向數(shù)據(jù)庫的API:
Java Driver API
秋泄,供開發(fā)商開發(fā)數(shù)據(jù)庫驅(qū)動(dòng)程序使用琐馆。
JDBC是sun公司提供一套用于數(shù)據(jù)庫操作的接口,java程序員只需要面向這套接口編程即可恒序。
不同的數(shù)據(jù)庫廠商瘦麸,需要針對這套接口,提供不同實(shí)現(xiàn)歧胁。不同的實(shí)現(xiàn)的集合滋饲,即為不同數(shù)據(jù)庫的驅(qū)動(dòng)。 ————面向接口編程
獲取數(shù)據(jù)庫連接
準(zhǔn)備工作:下載jar包喊巍,可以通過這個(gè)網(wǎng)站下載(Maven倉庫)屠缭,搜索mysql-connector-java
,選擇需要的版本崭参,下載jar包即可呵曹,本文使用的是mysql-connector-java-8.0.28
。
獲取數(shù)據(jù)庫連接的三要素:Driver接口的實(shí)現(xiàn)類,URL以及數(shù)據(jù)庫的用戶名密碼奄喂。
Driver接口
java.sql.Driver
接口是所有JDBC
驅(qū)動(dòng)程序需要實(shí)現(xiàn)的接口铐殃。這個(gè)接口是提供給數(shù)據(jù)庫廠商使用的,不同數(shù)據(jù)庫廠商提供不同的實(shí)現(xiàn)跨新。在程序中不需要直接去訪問實(shí)現(xiàn)了Driver
接口的類富腊,而是由驅(qū)動(dòng)程序管理器類(java.sql.DriverManager
)去調(diào)用這些Driver
實(shí)現(xiàn)。
- Oracle的驅(qū)動(dòng):
oracle.jdbc.driver.OracleDriver
- MySQL的驅(qū)動(dòng):
com.mysql.jdbc.Driver
注冊與加載驅(qū)動(dòng)
加載驅(qū)動(dòng):加載 JDBC 驅(qū)動(dòng)需調(diào)用Class
類的靜態(tài)方法forName()
域帐,向其傳遞要加載的JDBC
驅(qū)動(dòng)的類名赘被。
Class.forName("com.mysql.jdbc.Driver");
注冊驅(qū)動(dòng):DriverManager
類是驅(qū)動(dòng)程序管理器類,負(fù)責(zé)管理驅(qū)動(dòng)程序肖揣。
- 使用
DriverManager.registerDriver(com.mysql.jdbc.Driver)
來注冊驅(qū)動(dòng)
URL
JDBC URL 用于標(biāo)識一個(gè)被注冊的驅(qū)動(dòng)程序民假,驅(qū)動(dòng)程序管理器通過這個(gè) URL 選擇正確的驅(qū)動(dòng)程序,從而建立到數(shù)據(jù)庫的連接许饿。
官方文檔中URL的通用格式:
protocol//[hosts][/database][?properties]
協(xié)議 //主機(jī)名稱 /數(shù)據(jù)庫名稱 ?其他參數(shù)
也有把JDBC URL的標(biāo)準(zhǔn)看成是由三部分組成阳欲,各部分間用冒號分隔:
- jdbc(協(xié)議):子協(xié)議:子名稱
- 協(xié)議(jdbc):JDBC URL中的協(xié)議總是
jdbc
- 子協(xié)議:子協(xié)議用于標(biāo)識一個(gè)數(shù)據(jù)庫驅(qū)動(dòng)程序,如:mysql陋率、oracle
- 子名稱:一種標(biāo)識數(shù)據(jù)庫的方法球化。子名稱可以依不同的子協(xié)議而變化,用子名稱的目的是為了定位數(shù)據(jù)庫提供足夠的信息瓦糟。包含主機(jī)名(對應(yīng)服務(wù)端的ip地址)筒愚,端口號,數(shù)據(jù)庫名
- 協(xié)議(jdbc):JDBC URL中的協(xié)議總是
幾種常用的數(shù)據(jù)庫的JDBC URL
-
MySQL的連接URL編寫方式:
jdbc:mysql://主機(jī)名稱:mysql服務(wù)端口號/數(shù)據(jù)庫名稱?參數(shù)=值&參數(shù)=值 jdbc:mysql://localhost:3306/test jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8(如果JDBC程序與服務(wù)器端的字符集不一致菩浙,會導(dǎo)致亂碼巢掺,那么可以通過參數(shù)指定服務(wù)器端的字符集) jdbc:mysql://localhost:3306/test?user=root&password=root
-
Oracle 9i的連接URL編寫方式:
jdbc:oracle:thin:@主機(jī)名稱:oracle服務(wù)端口號:數(shù)據(jù)庫名稱 jdbc:oracle:thin:@localhost:1521:test
-
SQLServer的連接URL編寫方式:
jdbc:sqlserver://主機(jī)名稱:sqlserver服務(wù)端口號:DatabaseName=數(shù)據(jù)庫名稱 jdbc:sqlserver://localhost:1433:DatabaseName=test
用戶名和密碼
user
和password
可以用屬性名=屬性值
的方式告訴數(shù)據(jù)庫
也可以通過調(diào)用DriverManager
類的getConnection(url, user, password)
方法將用戶名和密碼告訴數(shù)據(jù)庫,與數(shù)據(jù)庫建立連接劲蜻。
連接數(shù)據(jù)庫的方式
聲明:不是說有很多種連接方式陆淀,而是為了展示一步一步對連接數(shù)據(jù)庫的寫法進(jìn)行迭代,為了區(qū)分先嬉,才說成有多種方式轧苫,最后我們會得出一個(gè)最終(最優(yōu))的寫法
連接方式一
@Test
public void testConnection() throws SQLException {
// 1. 獲取Driver實(shí)現(xiàn)類對象,使用數(shù)據(jù)庫廠商提供的類
Driver driver = new com.mysql.jdbc.Driver();
/*
* jdbc:mysql 協(xié)議
* localhost ip地址
* 3306 默認(rèn)的mysql的端口號
* test test數(shù)據(jù)庫
* */
String url = "jdbc:mysql://localhost:3306/test";
Properties info = new Properties();
// 將用戶名和密碼封裝在Properties中
info.setProperty("user", "root");
info.setProperty("password", "root");
Connection conn = driver.connect(url, info);
System.out.println(conn);
}
注意疫蔓,這里有個(gè)問題含懊,如果使用的連接器(mysql-connector-java
)版本在6以下,使用的驅(qū)動(dòng)類就是com.mysql.jdbc.Driver
衅胀,如果連接器版本是6以及6以上岔乔,使用的驅(qū)動(dòng)類就是com.mysql.cj.jdbc.Driver
,如果依然使用過的是com.mysql.jdbc.Driver
滚躯,那么就會有個(gè)警告性的錯(cuò)誤提示:
Loading class 'com.mysql.jdbc.Driver'. This is deprecated. The new driver class is 'com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
出現(xiàn)這個(gè)提示雏门,并不是說com.mysql.jdbc.Driver
完全不能使用嘿歌,而是不推薦使用,官方更推薦使用com.mysql.cj.jdbc.Driver
剿配。使用com.mysql.jdbc.Driver
也可以連接上數(shù)據(jù)庫并進(jìn)行操作搅幅,如果使用的連接器是6以上的版本阅束,還是按照官方推薦的呼胚,使用com.mysql.cj.jdbc.Driver
較好。
連接器版本在6以下息裸,com.mysql.jdbc.Driver
類的源碼:
// com.mysql.jdbc.Driver實(shí)現(xiàn)了java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
連接器版本在6以上蝇更,com.mysql.jdbc.Driver
類的源碼:
// com.mysql.jdbc.Driver繼承了com.mysql.cj.jdbc.Driver,所以com.mysql.jdbc.Driver類依然可以使用呼盆,但是不推薦年扩。
public class Driver extends com.mysql.cj.jdbc.Driver {
public Driver() throws SQLException {
}
static {
System.err.println("Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.");
}
}
com.mysql.cj.jdbc.Driver
的源碼:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
注意事項(xiàng)
如果使用com.mysql.cj.jdbc.Driver
驅(qū)動(dòng)類,URL中沒有設(shè)置useSSL=false
访圃,則會在連接數(shù)據(jù)庫的時(shí)候出現(xiàn)以下提示:
WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements
SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate
property is set to 'false'.You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate
verification.
URL中如果沒有設(shè)置serverTimezone=Asia/Shanghai
厨幻,則會在連接數(shù)據(jù)庫的時(shí)候出現(xiàn)以下提示:
The server time zone value '???ú±ê×??±??' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
為什么不設(shè)置serverTimezone=UTC
,是因?yàn)閁TC(世界標(biāo)準(zhǔn)時(shí)間)和北京時(shí)間相差8個(gè)小時(shí)腿时,所以具體設(shè)置哪個(gè)時(shí)區(qū)的時(shí)間况脆,請根據(jù)實(shí)際需求設(shè)置。
我在例子中URL并沒有設(shè)置useSSL和serverTimezone批糟,并沒有報(bào)錯(cuò)格了。我也沒有深究原因(我猜測是沒有使用框架),如果出現(xiàn)了上述提到的錯(cuò)誤徽鼎,那就加上相應(yīng)的參數(shù)即可盛末。
連接方式二
方式一中的代碼,我直接使用了new com.mysql.jdbc.Driver()
第三方的API否淤,程序的依賴性強(qiáng)悄但。
現(xiàn)在對方式一進(jìn)行迭代:
// 方式二:對方式一的迭代,在如下程序中不出現(xiàn)第三方的api石抡,使得程序具有更好的可移植性
@Test
public void testConnection2() throws Exception {
// 1. 獲取Driver實(shí)現(xiàn)類對象檐嚣,使用反射
Class<?> clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
// 2. 提供鏈接需要的用戶名和密碼
String url = "jdbc:mysql://localhost:3306/test";
// 3. 提供鏈接需要的用戶名和密碼
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "root");
// 4. 獲取鏈接
Connection conn = driver.connect(url, info);
System.out.println(conn);
}
連接方式三
對方式二的迭代:
// 方式三:使用DriverManager替換Driver
@Test
public void testConnection3() throws Exception {
// 1. 獲取Driver實(shí)現(xiàn)類對象,使用反射
Class<?> clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
// 2. 提供另外三個(gè)連接的基本信息
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root";
// 3. 注冊驅(qū)動(dòng)
DriverManager.registerDriver(driver);
// 4. 獲取鏈接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
DriverManager
顧名思義是驅(qū)動(dòng)管理類汁雷,使用它比直接使用Driver
更為方便净嘀。
連接方式四
對方式三進(jìn)行迭代:
// 方式四:可以只是加載驅(qū)動(dòng),不用顯示的注冊驅(qū)動(dòng)了侠讯。
@Test
public void testConnection4() throws Exception {
// 1. 獲取Driver實(shí)現(xiàn)類對象
Class.forName("com.mysql.jdbc.Driver");
//相較于方式3挖藏,可以省略注冊驅(qū)動(dòng)的操作
// 2. 注冊驅(qū)動(dòng)
// DriverManager.registerDriver(driver);
// 為什么?
/*
在 mysql的Driver實(shí)現(xiàn)類中厢漩,聲明了如下的操作:
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
*/
// 2. 提供另外三個(gè)連接的基本信息
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root";
// 3. 獲取鏈接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
連接方式五(最終方案)
在src目錄下新建配置文件jdbc.properties
膜眠,將數(shù)據(jù)庫連接需要的4個(gè)基本信息聲明在配置文件中:
user=root
password=root
url=jdbc:mysql://localhost:3306/test
driverClass=com.mysql.jdbc.Driver
注意,如果使用的連接器(mysql-connector-java
)是6及6以上的版本,driverClass
應(yīng)使用com.mysql.cj.jdbc.Driver
宵膨。
// 方式五:終極方案架谎,將數(shù)據(jù)庫連接需要的4個(gè)基本信息聲明在配置文件中,通過讀取配置文件的方式獲取鏈接
@Test
public void getConnection() throws Exception {
// 1. 讀取配置文件中的4個(gè)基本信息
InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
// 2. 加載驅(qū)動(dòng)
Class.forName(driverClass);
// 3. 獲取鏈接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
使用這種方式的好處:
- 實(shí)現(xiàn)了數(shù)據(jù)與代碼的分離辟躏,實(shí)現(xiàn)了解耦谷扣。
- 如果需要修改配置文件中的信息,可以避免程序重新編譯打過捎琐。