Java中幾種常用數(shù)據(jù)庫連接池的使用
一、應(yīng)用程序直接獲取數(shù)據(jù)庫連接的缺點
用戶每次請求都需要向數(shù)據(jù)庫獲得鏈接,而數(shù)據(jù)庫創(chuàng)建連接通常需要消耗相對較大的資源,創(chuàng)建時間也較長。假設(shè)網(wǎng)站一天10萬訪問量,數(shù)據(jù)庫服務(wù)器就需要創(chuàng)建10萬次連接,極大的浪費數(shù)據(jù)庫的資源,并且極易造成數(shù)據(jù)庫服務(wù)器內(nèi)存溢出、拓機。如下圖所示:

二、使用數(shù)據(jù)庫連接池優(yōu)化程序性能
2.1、數(shù)據(jù)庫連接池的基本概念
數(shù)據(jù)庫連接是一種關(guān)鍵的有限的昂貴的資源,這一點在多用戶的網(wǎng)頁應(yīng)用程序中體現(xiàn)的尤為突出.對數(shù)據(jù)庫連接的管理能顯著影響到整個應(yīng)用程序的伸縮性和健壯性,影響到程序的性能指標(biāo).數(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ù)量時,這些請求將被加入到等待隊列中.
數(shù)據(jù)庫連接池的最小連接數(shù)和最大連接數(shù)的設(shè)置要考慮到以下幾個因素:
- 最小連接數(shù):是連接池一直保持的數(shù)據(jù)庫連接,所以如果應(yīng)用程序?qū)?shù)據(jù)庫連接的使用量不大,將會有大量的數(shù)據(jù)庫連接資源被浪費.
- 最大連接數(shù):是連接池能申請的最大連接數(shù),如果數(shù)據(jù)庫連接請求超過次數(shù),后面的數(shù)據(jù)庫連接請求將被加入到等待隊列中,這會影響以后的數(shù)據(jù)庫操作
- 如果最小連接數(shù)與最大連接數(shù)相差很大:那么最先連接請求將會獲利,之后超過最小連接數(shù)量的連接請求等價于建立一個新的數(shù)據(jù)庫連接.不過,這些大于最小連接數(shù)的數(shù)據(jù)庫連接在使用完不會馬上被釋放,他將被放到連接池中等待重復(fù)使用或是空間超時后被釋放.
2.2、編寫數(shù)據(jù)庫連接池
編寫連接池需實現(xiàn)java.sql.DataSource接口。DataSource接口中定義了兩個重載的getConnection方法:
- Connection getConnection()
- Connection getConnection(String username, String password)
實現(xiàn)DataSource接口,并實現(xiàn)連接池功能的步驟:
- 在DataSource構(gòu)造函數(shù)中批量創(chuàng)建與數(shù)據(jù)庫的連接,并把創(chuàng)建的連接加入LinkedList對象中。
- 實現(xiàn)getConnection方法,讓getConnection方法每次調(diào)用時,從LinkedList中取一個Connection返回給用戶。
- 當(dāng)用戶使用完Connection,調(diào)用Connection.close()方法時,Collection對象應(yīng)保證將自己返回到LinkedList中,而不要把conn還給數(shù)據(jù)庫。Collection保證將自己返回到LinkedList中是此處編程的難點。
數(shù)據(jù)庫連接池核心代碼
使用動態(tài)代理技術(shù)構(gòu)建連接池中的connection

JdbcPool.java:
大致思路:1、讀取配置文件,將屬性值取出
2、注冊jdbc驅(qū)動
3、通過數(shù)據(jù)庫連接數(shù)和驅(qū)動管理器獲得相應(yīng)連接(.getConnection()),因為DataSource是接口,所以這個方法需要 我們手動實現(xiàn)
4、重點:實現(xiàn)getConnection()方法
思考:當(dāng)外部有連接需求時,直接從connectList拿出一個connect,就實現(xiàn)了這個方法,但是我們再想想,它用完了怎么回收呢,確實,有close()方法,但是這個方法作用是將這個連接還給數(shù)據(jù)庫,而不是數(shù)據(jù)庫連接池,這樣會導(dǎo)致數(shù)據(jù)庫連接池中 的連接越來越少,這樣可不行,但是我們也不能影響它的正常使用吧,在這種情況下,我們想要監(jiān)測這個連接對象的動態(tài),在它調(diào)用close()方法時,我們將其再添加進connectList,這樣連接池中的連接不就沒少了嗎,在java中對于監(jiān)聽一個對象的動態(tài),最常用也最實用的便是動態(tài)代理,對于動態(tài)代理,其實我們可以把它想象成就是一個大盒子,里面裝著一個真實對象小盒子,這就在大盒子和小盒子間形成了一個橫切空隙,而真實做事的還是這個對象,代理只是個接活的,接到活就給內(nèi)部的小盒子干,自然這個空隙可以用來監(jiān)測真實對象的事務(wù),也就是監(jiān)聽對象的方法。具體可以看看這個,動態(tài)代理:http://www.dbjr.com.cn/article/213416.htm
package jdbcPoolTest;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.Properties;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.omg.PortableServer.POA;
public class JdbcPool implements DataSource{
private static LinkedList<Connection> listConnections = new LinkedList<>();
static {
//靜態(tài)代碼塊中加載db.properties配置文件
InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
try {
//讀取文件內(nèi)容
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
//數(shù)據(jù)庫連接池的初始化連接大小
int jdbcPoolInitSize = Integer.parseInt(prop.getProperty("jdbcPoolInitSize"));
//加載數(shù)據(jù)庫驅(qū)動
Class.forName(driver);
for(int i=0; i<jdbcPoolInitSize; i++) {
//獲取連接
Connection conn = DriverManager.getConnection(url, username, password);
System.out.println("獲取連接:"+conn);
//將conn連接加入listConnections集合中,此時的listConnections就是一個連接池
listConnections.add(conn);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public Connection getConnection() throws SQLException {
// TODO Auto-generated method stub
//如果數(shù)據(jù)庫連接池的連接數(shù)大于0
System.out.println("進來了");
if (listConnections.size()>0) {
//從listConnections中獲取一個連接
final Connection conn = listConnections.removeFirst();
System.out.println("數(shù)據(jù)庫連接池的大小為:"+listConnections.size());
//返回Connection的代理,利用代理可以處理一些橫切事件
System.out.println("取出的連接為:"+conn);
return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
//如果不是執(zhí)行關(guān)閉操作,則通過反射執(zhí)行相應(yīng)方法
if (!method.getName().equals("close")) {
return method.invoke(conn, args);
}else {
//否則,將conn歸還給連接池
listConnections.add(conn);
System.out.println("歸還連接:"+conn);
System.out.println("連接池大小為:"+listConnections.size());
return null;
}
}
});
}else {
throw new RuntimeException("對不起,數(shù)據(jù)庫正忙");
}
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setLogWriter(PrintWriter arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public boolean isWrapperFor(Class<?> arg0) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public <T> T unwrap(Class<T> arg0) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
寫一個JdbcUtil測試數(shù)據(jù)庫連接池
package jdbcPoolTest;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtil {
private static JdbcPool jdbcPool = new JdbcPool();
public static Connection getConnection() throws SQLException {
return jdbcPool.getConnection();
}
//釋放的資源包括Connection數(shù)據(jù)庫連接對象,負(fù)責(zé)執(zhí)行SQL命令的Statement對象,存儲查詢結(jié)果的ResultSet對象
public static void release(Connection conn,Statement st ,ResultSet rs) {
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (st!=null) {
try {
st.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (rs!=null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rs = null;
}
}
}
db.properties配置文件如下:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/student username=root password=123456 jdbcPoolInitSize=10
三、開源數(shù)據(jù)庫連接池
現(xiàn)在很多WEB服務(wù)器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的實現(xiàn),即連接池的實現(xiàn)。通常我們把DataSource的實現(xiàn),按其英文含義稱之為數(shù)據(jù)源,數(shù)據(jù)源中都包含了數(shù)據(jù)庫連接池的實現(xiàn)。
也有一些開源組織提供了數(shù)據(jù)源的獨立實現(xiàn):
- DBCP 數(shù)據(jù)庫連接池
- C3P0 數(shù)據(jù)庫連接池
在使用了數(shù)據(jù)庫連接池之后,在項目的實際開發(fā)中就不需要編寫連接數(shù)據(jù)庫的代碼了,直接從數(shù)據(jù)源獲得數(shù)據(jù)庫的連接。
3.1、DBCP數(shù)據(jù)源
DBCP 是 Apache 軟件基金組織下的開源連接池實現(xiàn),要使用DBCP數(shù)據(jù)源,需要應(yīng)用程序應(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)用程序獨立使用。
3.2、在應(yīng)用程序中加入dbcp連接池
1.導(dǎo)入相關(guān)jar包
commons-dbcp-1.2.2.jar、commons-pool.jar、common-logging.jar三個包
2、在類目錄下加入dbcp的配置文件:dbcpconfig.properties
用了官方的連接池,自然配置文件內(nèi)容也就多了
dbcpconfig.properties的配置信息如下:
注意這段:
#driver default 指定由連接池所創(chuàng)建的連接的只讀(read-only)狀態(tài)。
#如果沒有設(shè)置該值,則“setReadOnly”方法將不被調(diào)用。(某些驅(qū)動并不支持只讀模式,如:Informix)
defaultReadOnly=
如果設(shè)置為ture,那就只能進行查詢操作
#連接設(shè)置 報錯com.mysql.jdbc.Connection.isValid(I)Z,更新mysql包 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/student username=root password=123456 #初始化連接池大小 jdbcPoolInitSize=10 #最大連接數(shù)量 maxActive=50 #<!-- 最大空閑連接 --> maxIdle=20 #<!-- 最小空閑連接 --> minIdle=5 #<!-- 超時等待時間以毫秒為單位 6000毫秒/1000等于60秒 --> maxWait=60000 #JDBC驅(qū)動建立連接時附帶的連接屬性的格式必須為這樣:[屬性名=property;] #注意:"user" 與 "password" 兩個屬性會被明確地傳遞,因此這里不需要包含他們。 connectionProperties=useUnicode=true;characterEncoding=UTF8 #指定由連接池所創(chuàng)建的連接的自動提交(auto-commit)狀態(tài)。 defaultAutoCommit=true #driver default 指定由連接池所創(chuàng)建的連接的只讀(read-only)狀態(tài)。 #如果沒有設(shè)置該值,則“setReadOnly”方法將不被調(diào)用。(某些驅(qū)動并不支持只讀模式,如:Informix) defaultReadOnly= #driver default 指定由連接池所創(chuàng)建的連接的事務(wù)級別(TransactionIsolation)。 #可用值為下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=READ_UNCOMMITTED
包結(jié)構(gòu):

jdbcUtils_DBCP:
大致思路:
1、從配置文件中獲取輸入流,進行加載
2、有了BasicDataSoureFactory,那我們也不需要將配置文件中的屬性值一個一個讀取出來了,它有個方法createDataSource()能自動獲取到數(shù)據(jù)源
3、接下來只需要拿著這個數(shù)據(jù)源對象ds去獲取連接了,具體如何連接,就不需要我們關(guān)心,大致思路與我們自行編寫連接池類似,只不過多了些其他屬性的出來方法。從而簡化了我們的開發(fā)
package zdb.jdbc.util;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
public class jdbcUtils_DBCP {
/**
* 在java中,編寫數(shù)據(jù)庫連接池需實現(xiàn)java.sql.DataSource接口,每一種數(shù)據(jù)庫連接池都是DataSource接口的實現(xiàn)
* DBCP連接池就是java.sql.DataSource接口的一個具體實現(xiàn)
*/
private static DataSource ds = null;
static {
InputStream in = jdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
try {
prop.load(in);
//創(chuàng)建數(shù)據(jù)源
ds = BasicDataSourceFactory.createDataSource(prop);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
//釋放的資源包括Connection數(shù)據(jù)庫連接對象,負(fù)責(zé)執(zhí)行SQL命令的Statement對象,存儲查詢結(jié)果的ResultSet對象
public static void release(Connection conn,PreparedStatement ps ,ResultSet rs) {
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (ps!=null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (rs!=null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rs = null;
}
}
}
接下來我們通過在表中插入屬性值來測試--DataSourceTest:
注意這段:
//由于包版本問題:必須加上PreparedStatement.RETURN_GENERATED_KEYS,不然將會報主鍵錯誤
ps = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
package zdb.jdbc.util;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javafx.css.PseudoClass;
public class DataSourceTest {
public static void jdbcDataSource() {
PreparedStatement ps = null;
ResultSet rs = null;
Connection conn = null;
try {
//conn = jdbcUtils_DBCP.getConnection();
conn = jdbcUtils_DBCP.getConnection();
String sql = "insert into user(uname) values(?)";
//由于版本兼容問題:必須加上PreparedStatement.RETURN_GENERATED_KEYS
ps = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
ps.setString(1, "lonewolf");
ps.executeUpdate();
//獲得主鍵
rs = ps.getGeneratedKeys();
if (rs.next()) {
System.out.println(rs.getInt(1));
}
} catch (SQLException e) {
// TODO Auto-generated catch blockrs
e.printStackTrace();
}
finally {
jdbcUtils_DBCP.release(conn, ps, rs);
//jdbcUtils_C3P0.release(conn, ps, rs);
}
}
public static void main(String[] args) {
new DataSourceTest().jdbcDataSource();
}
}
3.3、C3P0數(shù)據(jù)源(重點)
C3P0是一個開源的JDBC連接池,它實現(xiàn)了數(shù)據(jù)源和JNDI綁定,支持JDBC3規(guī)范和JDBC2的標(biāo)準(zhǔn)擴展。目前使用它的開源項目有Hibernate,Spring等。C3P0數(shù)據(jù)源在項目開發(fā)中使用得比較多。
c3p0與dbcp區(qū)別
dbcp默認(rèn)不自動回收空閑連接,需要手動開啟c3p0默認(rèn)自動回收空閑連接功能3.4、在應(yīng)用程序中加入C3P0連接池
1.導(dǎo)入相關(guān)jar包
c3p0-0.9.5.2.jar、mchange-commons-java-0.2.12jar,如果操作的是Oracle數(shù)據(jù)庫,那么還需要導(dǎo)入c3p0-oracle-thin-extras-0.9.2-pre1.jar
注意:導(dǎo)入包時,兩個包的版本也是需要匹配的
不然會報錯:Exception in thread "main" java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector
但是這兩個包版本是匹配的,下載鏈接c3p0_jar_jb51.rar
2、包結(jié)構(gòu):

3、在類目錄下加入C3P0的配置文件:c3p0-config.xml,即在項目根目錄下讀取文件
c3p0-config.xml的配置信息如下:
注意:配置文件中有兩種內(nèi)容相似的配置,一種缺省配置,意思是在沒有指定配置文件名時,調(diào)用該配置,一種命名配置,也就是在使用配置文件時,加上配置名即可引用,如接下來會說的:new ComboPooledDataSource("MySQL");
<c3p0-config>
<!--
C3P0的缺省(默認(rèn))配置,
如果在代碼中“ComboPooledDataSource ds = new ComboPooledDataSource();”這樣寫就表示使用的是C3P0的缺省(默認(rèn))配置信息來創(chuàng)建數(shù)據(jù)源
-->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/student</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<!--
C3P0的命名配置,
如果在代碼中“ComboPooledDataSource ds = new ComboPooledDataSource("MySQL");”這樣寫就表示使用的是name是MySQL的配置信息來創(chuàng)建數(shù)據(jù)源
-->
<named-config name="MySQL">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/student</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
</c3p0-config>
jdbcUtils_C3P0:
大致思路:
我們可以通過代碼看到,c3p0將我們的代碼量又縮減了許多
ComboPooledDataSource ds = new ComboPooledDataSource("MySQL");我們直接new一個ComboPooledDataSource()對象就可以獲得數(shù)據(jù)源,所以這就是為什么之前需要將xml配置文件放在根目錄下,“MySQL”就是我們的配置的名字
package zdb.jdbc.util;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class jdbcUtils_C3P0 {
private static ComboPooledDataSource ds = null;
static {
ds = new ComboPooledDataSource("MySQL");
}
public static Connection getConnection() throws SQLException{
//從數(shù)據(jù)源中獲取數(shù)據(jù)庫連接
return ds.getConnection();
}
//釋放的資源包括Connection數(shù)據(jù)庫連接對象,負(fù)責(zé)執(zhí)行SQL命令的Statement對象,存儲查詢結(jié)果的ResultSet對象
public static void release(Connection conn,PreparedStatement ps ,ResultSet rs) {
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (ps!=null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (rs!=null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rs = null;
}
}
}
測試C3P0數(shù)據(jù)源-DataSourceTest
package zdb.jdbc.util;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javafx.css.PseudoClass;
public class DataSourceTest {
public static void jdbcDataSource() {
PreparedStatement ps = null;
ResultSet rs = null;
Connection conn = null;
try {
//conn = jdbcUtils_DBCP.getConnection();
conn = jdbcUtils_C3P0.getConnection();
String sql = "insert into user(uname) values(?)";
//由于版本兼容問題:必須加上PreparedStatement.RETURN_GENERATED_KEYS
ps = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
ps.setString(1, "lonewolf");
ps.executeUpdate();
//獲得主鍵
rs = ps.getGeneratedKeys();
if (rs.next()) {
System.out.println(rs.getInt(1));
}
} catch (SQLException e) {
// TODO Auto-generated catch blockrs
e.printStackTrace();
}
finally {
jdbcUtils_DBCP.release(conn, ps, rs);
//jdbcUtils_C3P0.release(conn, ps, rs);
}
}
public static void main(String[] args) {
new DataSourceTest().jdbcDataSource();
}
}
這些便是數(shù)據(jù)庫連接池的大致內(nèi)容。
接下來說說
四、Tomcat中配置數(shù)據(jù)庫源
在實際開發(fā)中,我們有時候還會使用服務(wù)器提供給我們的數(shù)據(jù)庫連接池,比如我們希望Tomcat服務(wù)器在啟動的時候可以幫我們創(chuàng)建一個數(shù)據(jù)庫連接池,那么我們在應(yīng)用程序中就不需要手動去創(chuàng)建數(shù)據(jù)庫連接池,直接使用Tomcat服務(wù)器創(chuàng)建好的數(shù)據(jù)庫連接池即可。要想讓Tomcat服務(wù)器在啟動的時候幫我們創(chuàng)建一個數(shù)據(jù)庫連接池,那么需要簡單配置一下Tomcat服務(wù)器。
4.1、JNDI技術(shù)簡介
JNDI(Java Naming and Directory Interface),Java命名和目錄接口,它對應(yīng)于J2SE中的javax.naming包,
這 套API的主要作用在于:它可以把Java對象放在一個容器中(JNDI容器),并為容器中的java對象取一個名稱,以后程序想獲得Java對象,只需 通過名稱檢索即可。其核心API為Context,它代表JNDI容器,其lookup方法為檢索容器中對應(yīng)名稱的對象。
Tomcat服務(wù)器創(chuàng)建的數(shù)據(jù)源是以JNDI資源的形式發(fā)布的,所以說在Tomat服務(wù)器中配置一個數(shù)據(jù)源實際上就是在配置一個JNDI資源
服務(wù)器創(chuàng)建好數(shù)據(jù)源之后,我們的應(yīng)用程序又該怎么樣得到這個數(shù)據(jù)源呢,Tomcat服務(wù)器創(chuàng)建好數(shù)據(jù)源之后是以JNDI的形式綁定到一個JNDI容器中的,我們可以把JNDI想象成一個大大的容器,我們可以往這個容器中存放一些對象,一些資源,JNDI容器中存放的對象和資源都會有一個獨一無二的名稱,應(yīng)用程序想從JNDI容器中獲取資源時,只需要告訴JNDI容器要獲取的資源的名稱,JNDI根據(jù)名稱去找到對應(yīng)的資源后返回給應(yīng)用程序。我們平時做javaEE開發(fā)時,服務(wù)器會為我們的應(yīng)用程序創(chuàng)建很多資源,比如request對象,response對象,服務(wù)器創(chuàng)建的這些資源有兩種方式提供給我們的應(yīng)用程序使用:第一種是通過方法參數(shù)的形式傳遞進來,比如我們在Servlet中寫的doPost和doGet方法中使用到的request對象和response對象就是服務(wù)器以參數(shù)的形式傳遞給我們的。第二種就是JNDI的方式,服務(wù)器把創(chuàng)建好的資源綁定到JNDI容器中去,應(yīng)用程序想要使用資源時,就直接從JNDI容器中獲取相應(yīng)的資源即可。
對于上面的name="jdbc/datasource"數(shù)據(jù)源資源,在應(yīng)用程序中可以用如下的代碼去獲取
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
dataSource = (DataSource)envCtx.lookup("jdbc/datasource");
此種配置下,數(shù)據(jù)庫的驅(qū)動jar文件需放置在tomcat的lib下
4.2、配置Tomcat數(shù)據(jù)源 包結(jié)構(gòu):

如果報錯com.mysql.jdbc.Connection.isValid(I)Z,說明包太舊了,更新mysql包即可解決
配置數(shù)據(jù)源JNDI的方式有很多,詳細請看:http://www.dbjr.com.cn/article/213425.htm
注意了:有個問題坑了我一天
先看我的這篇文章---解決Tomcat中修改server.xml和content.xml后自動還原問題:http://www.dbjr.com.cn/article/213429.htm
之前我是直接在外部的tomcat文件下的conf文件夾下去修改這兩個文件,所以落坑
看完這篇文章應(yīng)該知道了server.xml、context.xml的大概配置,接下來,我們來具體配置
我使用---配置全局JNDI數(shù)據(jù)源,應(yīng)用到所有Tomcat下部署的應(yīng)用
第一步、找到Tomcat的server.xml中GlobalNamingResources節(jié)點,在節(jié)點下加一個全局?jǐn)?shù)據(jù)源
注意:name="jdbc/mysql"中“jdbc/mysql”為數(shù)據(jù)源名,在文件中多處引用
<Resource name="jdbc/mysql" scope="Shareable" type="javax.sql.DataSource" factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory" url="jdbc:mysql://localhost:3306/test" driverClassName ="com.mysql.jdbc.Driver" username="root" password="root" />
第二步,找到Tomcat的context.xml,在Context節(jié)點下加一個ResourceLink節(jié)點對第一步配置的數(shù)據(jù)源進行引用
這個XML配置文件的根節(jié)點就是<Context>

插入代碼:
<ResourceLink global="jdbc/mysql" name="jdbc/mysql" type="javax.sql.DataSource"/>
第三步,配置web.xml文件,添加資源映射(此步驟可要可不要)
<resource-ref> <description>DB Connection</description> <res-ref-name>jdbc/mysql</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
這種配置方式
優(yōu)點:重用性,一次性到位
缺點:沒有可控性(tomcat原始文件遭到修改)
配置文件配置好了,現(xiàn)在就來寫代碼
JdbcUtils_JNDI:
package zdb.util.JNDI_tomcat;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class JdbcUtils_JNDI {
private static DataSource ds = null;
static {
try {
//初始化JNDI
Context initCtx = new InitialContext();
//得到JNDI容器
Context envCtx = (Context) initCtx.lookup("java:comp/env");
//從JNDI容器中檢索name為jdbc/datasource的數(shù)據(jù)源
ds = (DataSource) envCtx.lookup("jdbc/mysql");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException{
//從數(shù)據(jù)源中獲取數(shù)據(jù)庫連接
return ds.getConnection();
}
//釋放的資源包括Connection數(shù)據(jù)庫連接對象,負(fù)責(zé)執(zhí)行SQL命令的Statement對象,存儲查詢結(jié)果的ResultSet對象
public static void release(Connection conn,PreparedStatement ps ,ResultSet rs) {
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (ps!=null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (rs!=null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rs = null;
}
}
}
測試下
JNDI_test:
package zdb.util.JNDI_tomcat;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/JNDI_test")
public class JNDI_test extends HttpServlet{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
//獲取數(shù)據(jù)庫連接
conn = JdbcUtils_JNDI.getConnection();
System.out.println("mysql Connection pool connected !!");
String sql = "insert into user(uname) values(?)";
st = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
st.setString(1, "gacl");
st.executeUpdate();
//獲取數(shù)據(jù)庫自動生成的主鍵
rs = st.getGeneratedKeys();
if(rs.next()){
System.out.println(rs.getInt(1));
}
}catch (Exception e) {
e.printStackTrace();
}finally{
//釋放資源
JdbcUtils_JNDI.release(conn, st, rs);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
我再通過寫一個jsp頁面來映射到該servlet測試下
test.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style>
body{
text-align:center;
}
</style>
</head>
<body>
<a href="${pageContext.servletContext.contextPath }/JNDI_test" rel="external nofollow" >測試</a>
</body>
測試結(jié)果成功:

拓展:由于此種配置模式的缺點,我們還可以通過增加一個新的xml文件來增加節(jié)點,而不用動原始文件
具體請看:tomcat虛擬路徑的配置
幾篇文章推薦給大家看下:
tomcat下面web應(yīng)用發(fā)布路徑配置( 即虛擬目錄配置 ):http://www.voidcn.com/article/p-zctzsjte-rs.html
Tomcat服務(wù)器原理詳解:https://www.cnblogs.com/crazylqy/p/4706223.html
Tomcat中主目錄配置與虛擬目錄配置問題自我總結(jié):http://blog.51cto.com/longx/1357666
文章借鑒參照:-孤傲蒼狼-原文:https://www.cnblogs.com/xdp-gacl/p/4002804.html
到此這篇關(guān)于Java中幾種常用數(shù)據(jù)庫連接池的使用的文章就介紹到這了,更多相關(guān)Java 數(shù)據(jù)庫連接池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot項目中如何讓非Spring管理的類獲得一個注入的Bean
這篇文章主要介紹了Springboot項目中如何讓非Spring管理的類獲得一個注入的Bean問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
J2SE基礎(chǔ)之命令行中編寫第一個 Hello World
“Hello World”程序指的是只在計算機屏幕上輸出“Hello, World!”(意為“世界,你好!”)這行字符串的計算機程序。hello world作為所有編程語言的起始階段,占據(jù)著無法改變的地位,所有的編程第一步就在于此了!經(jīng)典之中的經(jīng)典!hello world!2016-05-05
Spring和SpringMVC父子容器關(guān)系初窺(小結(jié))
這篇文章主要介紹了Spring和SpringMVC父子容器關(guān)系初窺(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01

