從jdbc中獲取連線物件Connection的三種方法
方法1:使用DriverManager類
Connection con = null;
try{
//通過得到位元組碼物件的方式載入靜態程式碼塊 從而載入註冊驅動程式
Class.forName(String driver);
//註冊URL取得對資料庫的連線 獲取連線物件
con = DriverManager.getConnection(uri, username, password);
...
con.close();
}catch(Exception e){
...
}finally{
...
}
使用這種連線資料庫的方法,存在幾個問題。
首先,“JDBCDriverName”是硬編碼的,例如使用Oracle 資料庫,只能使用oracle.jdbc.driver.OracleDriver 驅動,如果根據需要,改用其他的資料庫產品例如DB2 或者MySQL,則JDBC 驅動程式包和類名需要做相應的修改。
其次,用於連線資料庫的URL、使用者名稱和密碼也是硬編碼的,這不僅使程式在不同的環境下不能正常執行,而且還存在嚴重的安全性問題,因為這些引數可能根據實際情況有所更改而且使用者名稱和密碼很容易遭到破解。
第三,應該使用連線池而不是自己管理連線。
方法2:配置容器資料來源
JNDI(Java Naming and Directory Interface),即Java 命名與自錄介面。JNDI為開發人員提供了查詢和訪問各種命名和目錄服務通用的、統一的介面,完全解決了上述問題.使用JNDI,開發人員不用關心類似“具體的資料庫後臺是什麼?JDBC 驅動程式是什麼?JDBC URL格式是什麼?訪問資料庫的使用者名稱和口令是什麼?”等等這些問題,開發人員編寫的程式不需要包含對JDBC 驅動程式的引用,沒有伺服器名稱,沒有使用者名稱稱或口令,開發人員僅僅需要引用相應的資料來源即可。
資料來源是在JDBC 2.0中引入的一個概念。在JDBC 2.0擴充套件包中定義了javax.sql.DataSource介面來描述這個概念,資料來源物件必須實現這個介面。如果使用者希望建立一個數據庫連線,通過查詢在JNDI 服務中的資料來源,就可以從資料來源中獲取相應的資料庫連線。這樣使用者就只需要提供一個邏輯名稱( Logic Name),而不是資料庫登入的具體細節。
不再如方法1那樣親自管理資料庫連線,而是讓Servlet/JSP 容器來替你完成,需要對容器進行配置。在Tomcat 中,是通過在應用程式的Context 元素下宣告Resource元素來實現的,Tomcat上下文中就包含了一個帶有內部連線池的DataSource 資源。
1.在tomcat伺服器conf資料夾下context.xml配置
<Context>
<Resource name="jdbc/myDataSource"
auth="Container"
type="javax.sql.DataSource"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test" />
<!-- Default set of monitored resources -->
<WatchedResource >WEB-INF/web.xml</WatchedResource>
</Context>
2.DataSourceCache類
這是一個輔助工具類,負責查詢容器管理的DataSource ,並進行快取。
package app10d.dao;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
/*DataSourceCache 類,這是一個輔助工具類,負責查詢容器管
理的DataSource ,並進行快取。*/
public class DataSourceCache {
private static DataSourceCache instance;
private DataSource dataSource;
static {
instance = new DataSourceCache();
}
private DataSourceCache() {
//該介面表示一個命名上下文,它由一組名稱對物件繫結組成。 它包含檢查和更新這些繫結的方法。
Context context = null;
try {
context = new InitialContext();
dataSource = (DataSource) context.lookup(
"java:comp/env/jdbc/myDataSource");
} catch (NamingException e) {
}
}
public static DataSourceCache getInstance() {
return instance;
}
public DataSource getDataSource() {
return dataSource;
}
}
3.BaseDao類
建立dataSource,就可以獲取到連線
package app10d.dao;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import javax.sql.DataSource;
public class BaseDAO implements DAO {
public Connection getConnection() throws DAOException {
//問題在於,這個連線來自一個由容器維護的JNDI物件。那麼在容器之
//外如何對ProductDAO 進行測試呢?
DataSource dataSource = DataSourceCache.getInstance().getDataSource();
try {
return dataSource.getConnection();
} catch (Exception e) {
e.printStackTrace();
throw new DAOException();
}
}
protected void closeDBObjects(ResultSet resultSet, Statement statement,
Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
}
}
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
}
}
}
}
配置容器資料來源,存在一個問題,就是難於測試。要想測試DAO物件,必須先將整個應用程式部署在一個容器中,並用一個瀏覽器輸入DAO 物件的值。這麼做會損失多少生產效率呢?於是目前最流行的獲取連線的方式就是利用一個依賴注入框架來管理資料庫連線。
方法3:依賴注入框架
使用Dependencylnjector 類代替依賴注入框架
使用c3p0連線池
package app10e.util;
import javax.sql.DataSource;
import app10e.action.GetProductsAction;
import app10e.action.SaveProductAction;
import app10e.dao.ProductDAO;
import app10e.dao.ProductDAOImpl;
import app10e.validator.ProductValidator;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;
public class DependencyInjector {
private DataSource dataSource;
/*Dependencylnjector 類硬編碼了許多值,包括JDBC URL 和資料庫的使用者名稱及密碼。
在框架中,屬性值一般來自可編輯的配置檔案,形式通常為XML 文件。在本例中我們把它簡單化了。*/
public void start() {
// create dataSource
ComboPooledDataSource cpds = new ComboPooledDataSource();
try {
cpds.setDriverClass("com.mysql.jdbc.Driver");
} catch (Exception e) {
e.printStackTrace();
}
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test");
cpds.setUser("root");
cpds.setPassword("root");
// to override default settings:
cpds.setMinPoolSize(5);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(20);
dataSource = cpds;
}
public void shutDown() {
// destroy dataSource
try {
DataSources.destroy(dataSource);
} catch (Exception e) {
e.printStackTrace();
}
}
/*為了從DependencyInjector 中獲得物件,需呼叫其getObject 方法,傳遞目標物件的Class。
*DependencyInjector 在app10e 中支援以下型別: ProductValidator、ProductDAO 、 GetProductsAction 及SaveProductAction 。
例如,為了獲得一個ProductDAO 例項,要通過傳遞ProductDAO .class 來呼叫getObject:*/
public Object getObject(Class type) {
if (type == ProductValidator.class) {
return new ProductValidator();
} else if (type == ProductDAO.class) {
return createProductDAO();
} else if (type == GetProductsAction.class) {
return createGetProductsAction();
} else if (type == SaveProductAction.class) {
return createSaveProductAction();
}
return null;
}
/*Dependencylnjector (及所有依賴注入框架)的魅力在於, 它所返回的物件也和依賴一同被注入了。如果某一個依賴中還具有其他的依賴,那麼這個依賴的依賴也會隨之一起被注入。*/
private GetProductsAction createGetProductsAction() {
GetProductsAction getProductsAction = new GetProductsAction();
// inject a ProductDAO to getProductsAction
getProductsAction.setProductDAO(createProductDAO());
return getProductsAction;
}
private SaveProductAction createSaveProductAction() {
SaveProductAction saveProductAction = new SaveProductAction();
// inject a ProductDAO to saveProductAction
saveProductAction.setProductDAO(createProductDAO());
return saveProductAction;
}
private ProductDAO createProductDAO() {
ProductDAO productDAO = new ProductDAOImpl();
// inject a DataSource to productDAO
productDAO.setDataSource(dataSource);
return productDAO;
}
}
通過使用依賴注入框架,可以獲得如下好處:
1. 可以很方便的在xml中修改資料庫連線設定
2. 方便進行dao的測試
3. dataSource是放在依賴注入框架中,可以很方便地先注入到dao物件中,再注入到action物件中,即某一個依賴中還具有其他的依賴,那麼這個依賴的依賴也會隨之一起被注入
通過案例1與案例3的對比,也可以體現出DataSource相比DriverManager有以下好處,所以應優先使用。
1. 應用程式不必硬編碼一個驅動程式類。
2. 可以對資料來源的屬性進行修改,這就意味著當資料來源或者驅動程式改變時,不必修改應用程式的程式碼。
3. 通過與中間層協同工作的DataSource類(比如使用了C3P0連線池)就可實現連線池和分散式事務處理。