本文目錄
學習小結
1、應用程序直接獲取連接的缺點(圖解)
2、使用數據庫連接池優化程序性能(圖解)
3、可擴展增強某個類方法的功能的三種方式
4、自定義數據庫連接池——基於裝飾設計模式
5、數據庫連接池核心代碼——基於動態代理技術
6、開源數據庫連接池介紹
7、DBCP數據源
8、DBCP數據源與應用服務器整合使用—— 配置Tomcat數據源
9、C3P0 數據源
10、JNDI技術簡介
相關學習
JDBC 學習筆記(一)—— 基礎知識 + 分頁技術
鏈接地址:http://even2012.iteye.com/blog/1886946
JDBC 學習筆記(二)—— 大數據+存儲過程+批處理+事務管理
鏈接地址:http://even2012.iteye.com/blog/1886950
JDBC 學習筆記(三)—— 數據源(數據庫連接池):DBCP數據源、C3P0 數據源以及自定義數據源技術
鏈接地址:http://even2012.iteye.com/blog/1886953
JDBC 學習筆記(四)—— 自定義JDBC框架+Apache—DBUtils框架+事務管理+操作多表
鏈接地址:http://even2012.iteye.com/blog/1886956
學習小結
1、應用程序直接獲取連接的缺點(圖解)
2、使用數據庫連接池優化程序性能(圖解)
3、可擴展增強某個類方法的功能的三種方式
(1) 在實際開發中,發現對象的方法滿足不了開發需求時,有三種方式對其進行增強:
(a)創建該類的子類,並覆蓋相應的方法;(較少使用)
(b)使用裝飾(包裝)設計模式;(可以使用,但有時書寫的方法太多)
(c)使用動態代理技術。(最優的方式。)
(2) 使用子類覆蓋父類方法的方式來增強功能的弊端:
需要將被增強父類的其他所需信息也要傳遞到子類中,而在開發中,經常無法知曉這些所需信息,所以使用子類覆蓋被增強類方法的方式只是用於被增強類的內容較爲簡單的情景。
(3) 使用包裝設計模式增強某個類方法的步驟:
(a) 定義一個類,實現與被增強類相同的接口;
(b) 在類中定義一個變量,記住被增強對象;
(c) 定義一個構造函數,接收被增強對象;
(d) 覆蓋想增強的方法;
(e) 對於不想增強的方法,直接調用目標對象(被增強對象)的方法。
4、自定義數據庫連接池——基於裝飾設計模式
編寫連接池需實現java.sql.DataSource接口。DataSource接口中定義了兩個重載的getConnection方法:
Connection getConnection()
Connection getConnection(String username, String password)
實現DataSource接口,並實現連接池功能的步驟:
(1) 在DataSource構造函數中批量創建與數據庫的連接,並把創建的連接加入LinkedList對象中。
(2) 實現getConnection方法,讓getConnection方法每次調用時,從LinkedList中取一個Connection返回給用戶。
(3) 當用戶使用完Connection,調用Connection.close()方法時,Collection對象應保證將自己返回到LinkedList中,而不要把conn還給數據庫。
Collection保證將自己返回到LinkedList中是此處編程的難點。
Demo樣例:自定義數據庫連接池 (附件:JdbcPool.java)
public class JdbcPool implements DataSource {
private static LinkedList<Connection> list = new LinkedList<Connection>();
private static Properties config = new Properties();
static{
try {
config.load(JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("db.properties"));
// 以配置文件方式 讀取數據庫配置信息。
Class.forName(config.getProperty("driver"));
for(int i=0;i<10;i++){
Connection conn = DriverManager.getConnection(config.getProperty("url"), config.getProperty("username"), config.getProperty("password"));
list.add(conn);
}
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
//conn.close() 此方法會將連接返回給數據庫,所以不可用,需要自定義增強其功能,將連接返回到List集合中。
/* 在實際開發,發現對象的方法滿足不了開發需求時,有三種方式對其進行增強
* 1.寫一個connecton子類,覆蓋close方法,增強close方法
* 2.用包裝設計模式
* 3.用動態代理 aop 面向切面編程
*/
public Connection getConnection() throws SQLException {
if(list.size()<=0){
throw new RuntimeException("數據庫忙,請稍會再來!!");
}
Connection conn = list.removeFirst(); //mysqlconnection C
MyConnection my = new MyConnection(conn); 調用自定義鏈接。
return my; //my-------preparedStatement commit createStatement close
}
// 內部類,當然也可以使用外部類
class MyConnection implements Connection{ //1.定義一個類,實現與被增強相同的接口
private Connection conn; //2.在類中定義一個變量,記住被增強對象
public MyConnection(Connection conn){ //3.定義一個構造函數,接收被增強對象
this.conn = conn;
}
public void close(){ //4.覆蓋想增強的方法
list.add(this.conn);
}
//5.對於不想增強的方法,直接調用目標對象(被增強對象)的方法
public void clearWarnings() throws SQLException {
this.conn.clearWarnings();
}
public void commit() throws SQLException {
this.conn.commit();
}
public Statement createStatement() throws SQLException {
return this.conn.createStatement();
}
以下省略其他32個 不想增強的方法。(裝飾模式的缺點,會實現許多不需要增強的方法)
………………
}
public Connection getConnection(String username, String password)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
public void setLogWriter(PrintWriter arg0) throws SQLException {
// TODO Auto-generated method stub
}
public void setLoginTimeout(int arg0) throws SQLException {
// TODO Auto-generated method stub
}
}
5、數據庫連接池核心代碼——基於動態代理技術
使用動態代理技術構建連接池中的connection
proxyConn = (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), conn.getClass().getInterfaces(),
new InvocationHandler() {
//此處爲內部類,當close方法被調用時將conn還回池中,其它方法直接執行
public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
if (method.getName().equals("close")) {
pool.addLast(conn);
return null;
}
return method.invoke(conn, args);
}
}
);
6、開源數據庫連接池介紹
現在很多WEB服務器(Weblogic, WebSphere, Tomcat)都提供了DataSource的實現,即連接池的實現。通常我們把DataSource的實現,按其英文含義稱之爲數據源,數據源中都包含了數據庫連接池的實現。
也有一些開源組織提供了數據源的獨立實現:
DBCP 數據庫連接池 (Tomcat內置)
C3P0 數據庫連接池(Spring內置)
實際應用時不需要編寫連接數據庫代碼,直接從數據源獲得數據庫的連接。程序員編程時也應儘量使用這些數據源的實現,以提升程序的數據庫訪問性能。
備註:簡單地講,使用開源數據源,就是爲了獲取其DataSource對象,然後通過該對象動態的地獲取數據庫連接。
7、DBCP數據源
DBCP 是 Apache 軟件基金組織下的開源連接池實現,使用DBCP數據源,應用程序應在系統中增加如下兩個 jar 文件:
Commons-dbcp.jar:連接池的實現
Commons-pool.jar:連接池實現的依賴庫
Tomcat 的連接池正是採用該連接池來實現的。該數據庫連接池既可以與應用服務器整合使用,也可由應用程序獨立使用。
使用DBCP示例代碼:
static{
InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in);
BasicDataSourceFactory factory = new BasicDataSourceFactory();
dataSource = factory.createDataSource(prop);
}
Demo樣例1:JDBC 數據庫連接池 由應用程序獨立使用。
public class JdbcUtils_DBCP {
private static DataSource ds = null;
static{
try{
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("2013-06-10 11:26");
Properties prop = new Properties();
prop.load(in);
BasicDataSourceFactory factory = new BasicDataSourceFactory();
ds = factory.createDataSource(prop);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close(); //throw new
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
st = null;
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
Demo樣例2: dbcpconfig.properties 文件的內容(也可參見附件)
#連接設置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day16
username=root
password=root
#<!-- 初始化連接 -->
initialSize=10
#最大連接數量
maxActive=50
#<!-- 最大空閒連接 -->
maxIdle=20
#<!-- 最小空閒連接 -->
minIdle=5
#<!-- 超時等待時間以毫秒爲單位 6000毫秒/1000等於60秒 -->
maxWait=60000
#JDBC驅動建立連接時附帶的連接屬性屬性的格式必須爲這樣:[屬性名=property;]
#注意:"user" 與 "password" 兩個屬性會被明確地傳遞,因此這裏不需要包含他們。
connectionProperties=useUnicode=true;characterEncoding=utf8
#指定由連接池所創建的連接的自動提交(auto-commit)狀態。
defaultAutoCommit=true
#driver default 指定由連接池所創建的連接的只讀(read-only)狀態。
#如果沒有設置該值,則“setReadOnly”方法將不被調用。(某些驅動並不支持只讀模式,如:Informix)
defaultReadOnly=
#driver default 指定由連接池所創建的連接的事務級別(TransactionIsolation)。
#可用值爲下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_COMMITTED
8、DBCP數據源與應用服務器整合使用—— 配置Tomcat數據源
查看Tomcat文檔(Tomcat-->TomcatDocument-->JNDI Resources-->定位到JDBC Data Sources),示例代碼:
<Context>
<Resource name="jdbc/datasource" auth="Container" type="javax.sql.DataSource"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/jdbc" maxActive="8" maxIdle="4" />
<Resource name="jdbc/EmployeeDB " auth="Container" type="javax.sql.DataSource"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/jdbc" maxActive="8" maxIdle="4" />
</Context>
放置位置:在MyEclipse中,在目錄/META-INF/下創建文件context.xml,將上述內容粘貼其中(注:不要寫XML文件的指令頭句)。該文件將被髮布到Tomcat服務器的conf\Catalina\localhost目錄中,並以工程名稱 命名該文件。
下面是調用JNDI,獲取存儲在JNDI容器中的資源的固定格式代碼(有關JNDI的知識,參見下一章節):
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
dataSource = (DataSource)envCtx.lookup("jdbc/datasource");
特別提醒:此種配置下,驅動jar文件需放置在tomcat的lib下
可以創建工具類JDBCUtils,java來封裝上面獲取連接的代碼。
Demo樣例: 封裝JNDI調用DataSource 獲取連接的代碼。
public class JdbcUtils_Tomcat {
private static DataSource ds;
static {
try {
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
}
9、C3P0 數據源
所需jar包:
c3p0-0.9.2-pre1.jar
mchange-commons-0.2.jar
c3p0-oracle-thin-extras-0.9.2-pre1.jar(注:連接oracle數據庫時才導入,否則不用導入。)
(1) 使用獨立程序編碼的方式配置c3p0數據源(有關數據庫的連接信息會被寫入代碼中,不夠優雅)
步驟:1、導入jar包:
2、編碼調用。
Demo:編碼調用c3p0數據源獲取連接。
ComboPooledDataSource ds=new ComboPooledDataSource();
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc1");
ds.setUser("root");
ds.setPassword("root");
ds.setMaxPoolSize(40);
ds.setMinPoolSize(10);
ds.setInitialPoolSize(30);
Connection conn=ds.getConnection();
其他數據庫的操作
………
(2) 使用配置文件的方式配置c3p0數據源
(a) 配置文件名稱:c3p0-config.xml (必須準確使用該名稱)
(b) 配置文件放置位置:src路徑下或者是web-inf/classes都可以(都一樣)。
(c) 配置文件名稱:c3p0-config.xml 的內容:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config> <!-- 默認數據庫連接池配置信息-->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<!-- 第二個數據庫連接池配置信息-->
<named-config name="flx"> <!--自定義數據源連接信息,比如可以分別書寫 mysql 和 oracle 兩個連接信息,以方便 換數據庫。 注:此處對應調用代碼中的“連接信息名” -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</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>
(d) 在頁面的調用代碼:ComboPooledDataSource ds =new ComboPooledDataSource(連接信息名);
備註:(a)此處對應 c3p0-config.xml 文件中 <named-config name="flx"> 元素——指定名稱的數據源。
(b)數據源名稱不寫,則自動在類路徑下搜索並使用 “默認數據源”;
(c)若書寫名稱,同樣會搜索並調用指定名稱的數據源;
Demo樣例:配置文件方式配置c3p0數據源的調用代碼 。
public class JdbcUtils_C3P0 {
private static ComboPooledDataSource ds = null;
static{
try{
ds = new ComboPooledDataSource(); //沒指定數據源名稱,則使用默認數據源
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close(); //throw new
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
st = null;
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
10、JNDI技術簡介
JNDI(Java Naming and Directory Interface),Java命名和目錄接口,它對應於J2SE中的javax.naming包,
這套API的主要作用在於:它可以把Java對象放在一個容器中(JNDI容器),併爲容器中的java對象取一個名稱,以後程序想獲得Java對象,只需通過名稱檢索即可。
其核心API爲Context,它代表JNDI容器,其lookup方法爲檢索容器中對應名稱的對象。
此部分內容簡單瞭解,會用其調用Tomcat內置的DBCP數據源即可。