數(shù)據(jù)庫連接JDBC
在Java中束世,數(shù)據(jù)庫存取技術(shù)可分為如下幾類:
JDBC直接訪問數(shù)據(jù)庫JDO技術(shù)第三方O/R工具薛耻,如Hibernate, ibatis 等JDBC是java訪問數(shù)據(jù)庫的基石输钩,JDO, Hibernate等只是更好的封裝了JDBC。
JDBC(Java Database Connectivity)是一個獨立于特定數(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ū)動程序的數(shù)據(jù)庫系統(tǒng)功戚,這樣就使得程序員無需對特定的數(shù)據(jù)庫系統(tǒng)的特點有過多的了解,從而大大簡化和加快了開發(fā)過程似嗤。
當(dāng)然也可以不使用統(tǒng)一的JDBC接口分別使用不同的數(shù)據(jù)庫廠商各自的驅(qū)動啸臀,但這樣就不能體現(xiàn)Java的可移植性了。
JDBC是sun公司提供一套用于數(shù)據(jù)庫操作的接口烁落,java程序員只需要面向這套接口編程即可乘粒。不同的數(shù)據(jù)庫廠商,需要針對這套接口伤塌,提供不同實現(xiàn)灯萍。不同的實現(xiàn)的集合,即為不同數(shù)據(jù)庫的驅(qū)動每聪。-----面向接口編程
即
JDBC:一套用于數(shù)據(jù)庫操作的接口
JDBC驅(qū)動:需要針對這套接口旦棉,提供不同實現(xiàn)齿风。不同的實現(xiàn)的集合,即為不同數(shù)據(jù)庫的驅(qū)動
JDBC接口(API)包括兩個層次:
面向應(yīng)用的API:Java API绑洛,抽象接口救斑,供應(yīng)用程序開發(fā)人員使用(連接數(shù)據(jù)庫,執(zhí)行SQL語句诊笤,獲得結(jié)果)系谐。
面向數(shù)據(jù)庫的API:Java Driver API,供開發(fā)商開發(fā)數(shù)據(jù)庫驅(qū)動程序用讨跟。
這是通過JDBC連接數(shù)據(jù)庫的幾種方式,也是從繁到簡的一個過程:
JDBCTest.java
public class JDBCTest {
//版本4.0 最終版
@Test
public void test4() throws Exception{
Properties pros = new Properties();
pros.load(this.getClass().getClassLoader().getResourceAsStream("com/atguigu/jdbc/jdbc.properties"));
String driverClassName = pros.getProperty("driverClassName");
String url = pros.getProperty("url");
String user = pros.getProperty("user");
String password = pros.getProperty("password");
//1. 加載驅(qū)動
Class.forName(driverClassName);
//2. 獲取連接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
//版本3.0
@Test
public void test3() throws Exception{
String driverClassName = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://127.0.0.1:3306/test";
String user = "root";
String password = "123456";
/*
* 通常不用顯式調(diào)用 DriverManager 類的 registerDriver()
* 方法來注冊驅(qū)動程序類的實例纪他,因為 Driver 接口的驅(qū)動程序類都包含了靜態(tài)代碼塊,
* 在這個靜態(tài)代碼塊中晾匠,隨類加載而加載茶袒,即Class類加載時就加載了驅(qū)動,所以它會
* 會調(diào)用 DriverManager.registerDriver() 方法來注冊自身的一個實例
*/
//1. 加載驅(qū)動
Class.forName(driverClassName);
//2. 獲取連接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
//版本2.0 利用 DriverManager(驅(qū)動管理類)凉馆,獲取數(shù)據(jù)庫連接
@Test
public void test2() throws Exception{
String driverClassName = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://127.0.0.1:3306/test";
String user = "root";
String password = "123456";
//1. 注冊驅(qū)動
Driver driver = null;
Class clazz = Class.forName(driverClassName);
driver = (Driver) clazz.newInstance();
DriverManager.registerDriver(driver);
//2. 獲取連接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
//版本1.0 利用反射薪寓,根據(jù) MySQL 廠商提供的驅(qū)動,獲取 MySQL 數(shù)據(jù)庫的連接
@Test
public void test1() throws Exception{
String driverClassName = "com.mysql.jdbc.Driver";
//1. 獲取驅(qū)動
Driver driver = null;
Class clazz = Class.forName(driverClassName);
driver = (Driver) clazz.newInstance();
//2. 獲取連接
String url = "jdbc:mysql://127.0.0.1:3306/test";
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "123456");
Connection conn = driver.connect(url, info);
System.out.println(conn);
}
}
數(shù)據(jù)庫連接池c3p0澜共、dhcp
在使用開發(fā)基于數(shù)據(jù)庫的web程序時向叉,傳統(tǒng)的模式(JDBC)基本是按以下步驟:
在主程序(如servlet、beans)中建立數(shù)據(jù)庫連接進(jìn)行sql操作斷開數(shù)據(jù)庫連接但這種模式開發(fā)嗦董,存在的問題:
普通的JDBC數(shù)據(jù)庫連接使用 DriverManager 來獲取母谎,每次向數(shù)據(jù)庫建立連接的時候都要將 Connection 加載到內(nèi)存中,再驗證用戶名和密碼(得花費0.05s~1s的時間)京革。需要數(shù)據(jù)庫連接的時候奇唤,就向數(shù)據(jù)庫要求一個,執(zhí)行完成后再斷開連接匹摇。這樣的方式將會消耗大量的資源和時間咬扇。數(shù)據(jù)庫的連接資源并沒有得到很好的重復(fù)利用.若同時有幾百人甚至幾千人在線,頻繁的進(jìn)行數(shù)據(jù)庫連接操作將占用很多的系統(tǒng)資源廊勃,嚴(yán)重的甚至?xí)斐煞?wù)器的崩潰懈贺。
對于每一次數(shù)據(jù)庫連接,使用完后都得斷開供搀。否則隅居,如果程序出現(xiàn)異常而未能關(guān)閉,將會導(dǎo)致數(shù)據(jù)庫系統(tǒng)中的內(nèi)存泄漏葛虐,最終將導(dǎo)致重啟數(shù)據(jù)庫胎源。
這種開發(fā)不能控制被創(chuàng)建的連接對象數(shù),系統(tǒng)資源會被毫無顧及的分配出去屿脐,如連接過多涕蚤,也可能導(dǎo)致內(nèi)存泄漏宪卿,服務(wù)器崩潰。
為解決傳統(tǒng)開發(fā)中的數(shù)據(jù)庫連接問題万栅,可以采用數(shù)據(jù)庫連接池技術(shù)佑钾。
數(shù)據(jù)庫連接池的基本思想就是為數(shù)據(jù)庫連接建立一個“緩沖池”。預(yù)先在緩沖池中放入一定數(shù)量的連接烦粒,當(dāng)需要建立數(shù)據(jù)庫連接時休溶,只需從“緩沖池”中取出一個,使用完畢之后再放回去扰她。
數(shù)據(jù)庫連接池負(fù)責(zé)分配兽掰、管理和釋放數(shù)據(jù)庫連接,它允許應(yīng)用程序重復(fù)使用一個現(xiàn)有的數(shù)據(jù)庫連接徒役,而不是重新建立一個孽尽。
數(shù)據(jù)庫連接池在初始化時將創(chuàng)建一定數(shù)量的數(shù)據(jù)庫連接放到連接池中,這些數(shù)據(jù)庫連接的數(shù)量是由最小數(shù)據(jù)庫連接數(shù)來設(shè)定的忧勿。無論這些數(shù)據(jù)庫連接是否被使用杉女,連接池都將一直保證至少擁有這么多的連接數(shù)量。連接池的最大數(shù)據(jù)庫連接數(shù)量限定了這個連接池能占有的最大連接數(shù)鸳吸,當(dāng)應(yīng)用程序向連接池請求的連接數(shù)超過最大連接數(shù)量時熏挎,這些請求將被加入到等待隊列中。
JDBC 的數(shù)據(jù)庫連接池使用 javax.sql.DataSource 來表示晌砾,DataSource 只是一個接口婆瓜,該接口通常由服務(wù)器(Weblogic, WebSphere, Tomcat)提供實現(xiàn),也有一些開源組織提供實現(xiàn):
DBCP 數(shù)據(jù)庫連接池C3P0 數(shù)據(jù)庫連接池DataSource 通常被稱為數(shù)據(jù)源贡羔,它包含連接池和連接池管理兩個部分,習(xí)慣上也經(jīng)常把 DataSource 稱為連接池
DataSource用來取代DriverManager來獲取Connection个初,獲取速度快乖寒,同時可以大幅度提高數(shù)據(jù)庫訪問速度。
DHCP數(shù)據(jù)源
DBCP 是 Apache 軟件基金組織下的開源連接池實現(xiàn)院溺,該連接池依賴該組織下的另一個開源系統(tǒng):Common-pool. 如需使用該連接池實現(xiàn)楣嘁,應(yīng)在系統(tǒng)中增加如下兩個 jar 文件:
Commons-dbcp.jar:連接池的實現(xiàn)
Commons-pool.jar:連接池實現(xiàn)的依賴庫
Tomcat 的連接池正是采用該連接池來實現(xiàn)的。該數(shù)據(jù)庫連接池既可以與應(yīng)用服務(wù)器整合使用珍逸,也可由應(yīng)用程序獨立使用逐虚。
數(shù)據(jù)源和數(shù)據(jù)庫連接不同,數(shù)據(jù)源無需創(chuàng)建多個谆膳,它是產(chǎn)生數(shù)據(jù)庫連接的工廠叭爱,因此整個應(yīng)用只需要一個數(shù)據(jù)源即可。
當(dāng)數(shù)據(jù)庫訪問結(jié)束后漱病,程序還是像以前一樣關(guān)閉數(shù)據(jù)庫連接:conn.close(); 但上面的代碼并沒有關(guān)閉數(shù)據(jù)庫的物理連接买雾,它僅僅把數(shù)據(jù)庫連接釋放把曼,歸還給了數(shù)據(jù)庫連接池。
兩種連接池分別有兩種方式創(chuàng)建數(shù)據(jù)庫的連接
DataSourceTest.java
public class DataSourceTest {
//使用 C3P0 方式二:
@Test
public void test4() throws SQLException{
DataSource ds = new ComboPooledDataSource("helloc3p0");
Connection conn = ds.getConnection();
System.out.println(conn);
}
//使用 C3P0 方式一:
@Test
public void test3() throws Exception{
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test");
cpds.setUser("root");
cpds.setPassword("123456");
Connection conn = cpds.getConnection();
System.out.println(conn);
}
//使用 DBCP 方式二:
@Test
public void test2() throws Exception{
Properties pros = new Properties();
pros.load(DataSourceTest.class.getClassLoader().getResourceAsStream("dbcp.properties"));
DataSource ds = BasicDataSourceFactory.createDataSource(pros);
Connection conn = ds.getConnection();
System.out.println(conn);
}
//使用 DBCP 方式一:
@Test
public void test1() throws SQLException{
BasicDataSource bds = new BasicDataSource();
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql://127.0.0.1:3306/test");
bds.setUsername("root");
bds.setPassword("123456");
bds.setInitialSize(10);
bds.setMaxActive(10);
Connection conn = bds.getConnection();
System.out.println(conn);
//將連接放回到連接池中
conn.close();
}
}
使用方式二的c3p0需要一個配置文件(當(dāng)然也可以用屬性文件)漓穿,且配置文件的名字必須是c3p0-config.xml嗤军,因為數(shù)據(jù)庫廠商底層是根據(jù)key-value來封裝數(shù)據(jù)的,所以會根據(jù)這個key來進(jìn)行匹配讀取晃危,而且必須放在src的路徑下叙赚,因為讀取是通過類加載器的方式進(jìn)行讀取的。使用方式二的dhcp需要一個屬性文件和jdbc的屬性文件大致一樣僚饭,就不多贅述了
c3p0-config.xml
<c3p0-config>
<named-config name="helloc3p0">
<!-- 連接數(shù)據(jù)庫的四個字符串 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/test</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 若連接池滿了一次增長幾個 -->
<property name="acquireIncrement">5</property>
<!-- 連接池初始大小 -->
<property name="initialPoolSize">10</property>
<!-- 連接池中最小連接數(shù) -->
<property name="minPoolSize">5</property>
<!-- 連接池中最大連接數(shù) -->
<property name="maxPoolSize">10</property>
<!-- 整個連接池中最多管理的 Statement 的個數(shù) -->
<property name="maxStatements">10</property>
<!-- 連接池中每個連接最多管理的 Statement 的個數(shù) -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
注意:配置文件的 的名字要和DataSource ds = new ComboPooledDataSource("helloc3p0");要傳入的參數(shù)一致震叮。