1. 程式人生 > >事務與連線池

事務與連線池

事務:
問題:事務是什麼,有什麼用?
事務就是一個事情,組成這個事情可能有多個單元,要求這些單元,要麼全都成功,要麼全都不成功。
在開發中,有事務的存在,可以保證資料完整性。
問題:事務怎樣操作
建立表:
create table account(
  id int primary key auto_increment,
  name varchar(20),
  money double
);
insert into account values(null,'aaa',1000);
insert into account values(null,'bbb',1000);
insert into account values(null,'ccc',1000);
1.mysql下怎樣操作

方式1:
start transaction  開啟事務
rollback 事務回滾
commit 事務提交

方式2:資料庫中存在一個自動提交變數 ,通過 show variables like'%commit%'; ---- autocommint 值是 on,說明開啟自動提交
show variables like '%commit%'; 可以檢視當前autocommit值
在mysql資料庫中它的預設值是"on"代表自動事務.
自動事務的意義就是:執行任意一條sql語句都會自動提交事務.
測試:將autocommit的值設定為off
1.set autocommit=off 關閉自動事務。

2.必須手動commit才可以將事務提交。

如果設定autocommit 為 off,意味著以後每條SQL 都會處於一個事務中,相當於每條SQL執行前 都執行 start transaction

注意:mysql預設autocommit=on  oracle預設的autocommit=off;
2.jdbc下怎樣操作
java.sql.Connection介面中有幾個方法是用於可以操作事務
1.setAutocommit(boolean flag);
如果flag=false;它就相當於start transaction;
2.rollBack()
事務回滾。
3.commit()

事務提交

設定事務回滾點

•   Savepoint sp =conn.setSavepoint();

•   Conn.rollback(sp);

•   Conn.commit();   //回滾後必須要提交

==============================================================================
事務特性(重點) ACID
1原子性(Atomicity)
原子性是指事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生。 
2一致性(Consistency)
事務前後資料的完整性必須保持一致。
3隔離性(Isolation)
事務的隔離性是指多個使用者併發訪問資料庫時,一個使用者的事務不能被其它使用者的事務所幹擾,多個併發事務之間資料要相互隔離。
4永續性(Durability)
永續性是指一個事務一旦被提交,它對資料庫中資料的改變就是永久性的,接下來即使資料庫發生故障也不應該對其有任何影響
-------------------------------------
如果不考慮事務的隔離性,會出現什麼問題?

1.髒讀 一個事務讀取到另一個事務的未提交資料。

2.不可重複讀

兩次讀取的資料不一致(update)

和髒讀的區別是,髒讀是讀取前一事務未提交的髒資料,不可重複讀是重新讀取了前一事務已提交的資料。3.虛讀(幻讀)
兩次讀取的資料一一致(insert)是指在一個事務內讀取到別的事務插入的資料,導致前後讀取不一致 4.丟失更新
兩個事務對同一條記錄進行操作,後提交的事務,將先提交的事務的修改覆蓋了。
-----------------------------------------
演示以上問題,以及問題的解決方案
對於以上的問題,我們可以通過設定事務的隔離級別來解決。
1.事務的隔離級別有哪些?
1 Serializable:可避免髒讀、不可重複讀、虛讀情況的發生。(序列化)
2 Repeatable read:可避免髒讀、不可重複讀情況的發生。(可重複讀)不可以避免虛讀
3 Read committed:可避免髒讀情況發生(讀已提交)
4 Read uncommitted:最低級別,以上情況均無法保證。(讀未提交)
2.怎樣設定事務的隔離級別?
1.mysql中設定 
1.檢視事務隔離級別
select @@tx_isolation查詢當前事務隔離級別
mysql中預設的事務隔離級別是  Repeatable read.
擴充套件:oracle 中預設的事務隔離級別是  Read committed
2.mysql中怎樣設定事務隔離級別
set session transaction isolation level 設定事務隔離級別
2.jdbc中設定
在jdbc中設定事務隔離級別
使用java.sql.Connection介面中提供的方法
void setTransactionIsolation(int level) throws SQLException
引數level可以取以下值:
level - 以下 Connection 常量之一:
Connection.TRANSACTION_READ_UNCOMMITTED、
Connection.TRANSACTION_READ_COMMITTED、
Connection.TRANSACTION_REPEATABLE_READ 
Connection.TRANSACTION_SERIALIZABLE。
(注意,不能使用 Connection.TRANSACTION_NONE,因為它指定了不受支援的事務。) 
--------------------------------------------------
3.演示
1.髒讀
一個事務讀取到另一個事務的未提交資料
設定A,B事務隔離級別為   Read uncommitted
set session transaction isolation level  read uncommitted;
1.在A事務中
start transaction;
update account set money=money-500 where name='aaa';
update account set money=money+500 where name='bbb';
2.在B事務中
start transaction;
select * from account;
這時,B事務讀取時,會發現,錢已經匯完。那麼就出現了髒讀。
當A事務提交前,執行rollback,在commit, B事務在查詢,就會發現,錢恢復成原樣
也出現了兩次查詢結果不一致問題,出現了不可重複讀.
2.解決髒讀問題
將事務的隔離級別設定為 read committed來解決髒讀
設定A,B事務隔離級別為   Read committed
set session transaction isolation level  read committed;
1.在A事務中
start transaction;
update account set money=money-500 where name='aaa';
update account set money=money+500 where name='bbb';
2.在B事務中
start transaction;
select * from account;
這時B事務中,讀取資訊時,是不能讀到A事務未提交的資料的,也就解決了髒讀。
讓A事務,提交資料 commit;
這時,在查詢,這次結果與上一次查詢結果又不一樣了,還存在不可重複讀。
3.解決不可重複讀
將事務的隔離級別設定為Repeatable read來解決不可重複讀。
設定A,B事務隔離級別為   Repeatable read;
set session transaction isolation level  Repeatable read;
1.在A事務中
start transaction;
update account set money=money-500 where name='aaa';
update account set money=money+500 where name='bbb';
2.在B事務中
start transaction;
select * from account;
當A事務提交後commit;B事務在查詢,與上次查詢結果一致,解決了不可重複讀。
4.設定事務隔離級別 Serializable ,它可以解決所有問題
set session transaction isolation level Serializable;
如果設定成這種隔離級別,那麼會出現鎖表。也就是說,一個事務在對錶進行操作時,
其它事務操作不了。
--------------------------------------------------
總結:
髒讀:一個事務讀取到另一個事務未提交資料
不可重複讀:兩次讀取資料不一致(讀提交資料)---update
虛讀:兩次讀取資料不一致(讀提交資料)----insert
事務隔離級別:
read uncommitted 什麼問題也解決不了.
read committed 可以解決髒讀,其它解決不了.
Repeatable read 可以解決髒讀,可以解決不可重複讀,不能解決虛讀.
Serializable 它會鎖表,可以解決所有問題.
安全性:serializable > repeatable read > read committed > read uncommitted 
效能 :serializable < repeatable read < read committed < read uncommitted 

結論: 實際開發中,通常不會選擇 serializable 和 read uncommitted ,
mysql預設隔離級別 repeatable read ,oracle預設隔離級別 read committed
==========================================================================================
案例:轉賬匯款----使用事務
問題:service呼叫了dao中兩個方法完成了一個業務操作,如果其中一個方法執行失敗怎樣辦?
需要事務控制
問題:怎樣進行事務控制?
我們在service層進行事務的開啟,回滾以及提交操作。
問題:進行事務操作需要使用Connection物件,那麼,怎樣保證,在service中與dao中所使用的是同一個Connection.
在service層創建出Connection物件,將這個物件傳遞到dao層.
注意:Connecton物件使用完成後,在service層的finally中關閉
而每一個PreparedStatement它們在dao層的方法中用完就關閉. 
關於程式問題
1.對於轉入與轉出操作,我們需要判斷是否成功,如果失敗了,可以通過丟擲自定義異常在servlet中判斷,
 進行資訊展示 。 
----------------------------------------------------------
問題:
在設定dao層時,
public interface AccountDao {
public void accountOut(String accountOut, double money) throws Exception;
public void accountIn(String accountIn, double money) throws Exception;
}
那麼我們自己去實現這個介面時,怎樣處理,同一個Connection物件問題?
使用ThreadLocal
ThreadLocal可以理解成是一個Map集合
Map<Thread,Object>
set方法是向ThreadLocal中儲存資料,那麼當前的key值就是當前執行緒物件.
get方法是從ThreadLocal中獲取資料,它是根據當前執行緒物件來獲取值。
如果我們是在同一個執行緒中,只要在任意的一個位置儲存了資料,在其它位置上,就可以獲取到這個資料。
關於JdbcUtils中使用ThreadLocal
1.宣告一個ThreadLocal
private static final ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
2.在getConnection()方法中操作
Connection con = tl.get(); 直接從ThreadLocal中獲取,第一次返回的是null.
if (con == null) {
// 2.獲取連線
con = DriverManager.getConnection(URL, USERNAME, PASSWORD);
tl.set(con); //將con裝入到ThreadLocal中。
}
==========================================================================================
丟失更新
多個事務對同一條記錄進行了操作,後提交的事務將先提交的事務操作覆蓋了。
檢視圖.
問題:怎樣解決丟失更新問題?
解決丟失更新可以採用兩種方式:
1.悲觀鎖
悲觀鎖 (假設丟失更新一定會發生 ) ----- 利用資料庫內部鎖機制,管理事務
提供的鎖機制
1.共享鎖
select * from table lock in share mode讀鎖、共享鎖)
2.排它鎖
select * from table for update 寫鎖、排它鎖)

update語句預設新增排它鎖

mysql資料庫內部提供兩種常用 鎖機制:共享鎖(讀鎖)和排它鎖(寫鎖)

允許一張資料表中資料記錄,新增多個共享鎖,新增共享鎖記錄,對於其他事務可讀不可寫的

一張資料表中資料記錄,只能新增一個排它鎖,在新增排它鎖的資料 不能再新增其他共享鎖和排它鎖的 ,對於其他事物可讀不可寫的

* 鎖必須在事務中新增 ,如果事務結束了 鎖就釋放了

2.樂觀鎖
樂觀鎖 (假設丟失更新不會發生)------- 採用程式中新增(時間戳欄位)版本欄位解決丟失更新問題
create table product (
  id int,
  name varchar(20),
  updatetime timestamp
);
insert into product values(1,'冰箱',null);

update product set name='洗衣機' where id = 1;

 timestamp 在插入和修改時 都會自動更新為當前時間

解決丟失更新:在資料表新增版本欄位,每次修改過記錄後,版本欄位都會更新,如果讀取是版本欄位,

與修改時版本欄位不一致,說明別人進行修改過資料 (重改)

===============================================================================================================

連線池

一次性建立多個連線,將多個連線快取在記憶體中 ,形成資料庫連線池(記憶體資料庫連線集合),如果應用程式需要操作資料庫,只需要從連線池中獲取一個連線,使用後,並不需要關閉連線,只需要將連線放回到連線池中。


缺點:使用者每次請求都需要向資料庫獲得連結,而資料庫建立連線通常需要消耗相對較大的資源,建立時間也較長。假設網站一天10萬訪問量,資料庫伺服器就需要建立10萬次連線,極大的浪費資料庫的資源,並且極易造成資料庫伺服器記憶體溢位、拓機。

問題:連線池是什麼,有什麼用?
連線池:就是建立一個容器,用於裝入多個Connection物件,在使用連線物件時,從容器中獲取一個Connection,
  使用完成後,在將這個Connection重新裝入到容器中。這個容器就是連線池。(DataSource)
  也叫做資料來源.   
我們可以通過連線池獲取連線物件.
優點:

節省建立連線與釋放連線 效能消耗 ---- 連線池中連線起到複用的作用 ,提高程式效能

原來由jdbcUtil建立連線,現在由dataSource建立連線,為實現不和具體資料為繫結,因此datasource也應採用配置檔案的方法獲得連線。

編寫連線池需實現javax.sql.DataSource介面。DataSource介面中定義了兩個過載的getConnection方法:Connection getConnection() Connection getConnection(String username,String password) 實現DataSource介面,並實現連線池功能的步驟:DataSource建構函式中批量建立與資料庫的連線,並把建立的連線儲存到一個集合物件實現getConnection方法,讓getConnection方法每次呼叫時,從集合物件中取一個Connection返回給使用者。當用戶使用完Connection,呼叫Connection.close()方法時,Collection物件應保證將自己返回到連線池的集合物件中,而不要把conn還給資料庫。擴充套件Connectionclose方法在關閉資料庫連線時,將connection存回連線池中,而並非真正的關閉擴充套件類的三種方式基於繼承--- 方法覆蓋使用裝飾模式包裝類,增強原有行為使用動態代理 --- 基於位元組碼Class在記憶體中執行過程----------------------------------------------------------------------------------自定義連線池
1.建立一個MyDataSource類,在這個類中建立一個LinkedList<Connection>
2.在其構造方法中初始化List集合,並向其中裝入5個Connection物件。
3.建立一個public Connection getConnection();從List集合中獲取一個連線物件返回.
4.建立一個  public void readd(Connection) 這個方法是將使用完成後的Connection物件重新裝入到List集合中.
程式碼問題:
1.連線池的建立是有標準的.
在javax.sql包下定義了一個介面 DataSource
簡單說,所有的連線池必須實現javax.sql.DataSource介面,
我們的自定義連線池必須實現DataSource介面。
2.我們操作時,要使用標準,怎樣可以讓 con.close()它不是銷燬,而是將其重新裝入到連線池.
要解決這個問題,其本質就是將Connection中的close()方法的行為改變。
怎樣可以改變一個方法的行為(對方法功能進行增強)

1.繼承 方法覆蓋

                                        注意:必須控制物件構造,才能使用繼承的方式對方法進行加強

2.裝飾模式
1.裝飾類與被裝飾類要實現同一個介面或繼承同一個父類
2.在裝飾類中持有一個被裝飾類引用

3.對方法進行功能增強。

3.動態代理
可以對行為增強

Proxy.newProxyInstance(ClassLoacer ,Class[],InvocationHandler);

原理:利用類載入器 ,在記憶體中根據目標類載入器和介面 建立一個代理物件 ,通過代理物件完成對原有物件方法進行加強

                                           注意:被代理物件,必須實現介面


結論:Connection物件如果是從連線池中獲取到的,那麼它的close方法的行為已經改變了,不在是銷燬,而是重新裝入到連線池。
--------------------------------------------------------------------
1.連線池必須實現javax.sql.DataSource介面。
2.要通過連線池獲取連線物件  DataSource介面中有一個  getConnection方法.

3.將Connection重新裝入到連線池   使用Connection的close()方法。

package cn.itcast.zq;
//使用繼承增強
public class Demo1 {
public static void main(String[] args) {
Person1 p=new Student1();
p.eat();
}

}
class Person1{
public void eat(){
System.out.println("吃兩個饅頭");
}
}
class Student1 extends Person1{
@Override
public void eat(){
super.eat();
System.out.println("加兩個雞腿");
}

}

package cn.itcast.zq;
//使用裝飾進行增強
public class Demo2 {
public static void main(String[] args) {
Car car=new Bmw();
//給車增強
CarDerector cd=new CarDerector(car);
cd.run();
}
}
interface Car {
void run();
}
class Bmw implements Car {
public void run() {
System.out.println("bmw run....");
}
}
class Benz implements Car {
public void run() {
System.out.println("benz run....");
}
}
// 使用裝飾來完成
class CarDerector implements Car {
private Car car;
public CarDerector(Car car) {
this.car = car;
}

public void run() {
System.out.println("新增導航");
car.run();

}

}

動態代理

package cn.itcast.zq;
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 cn.itcast.utils.JdbcUtils;
public class Demo3 {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
Class.forName("com.mysql.jdbc.Driver"); //註冊驅動
final Connection con = DriverManager.getConnection("jdbc:mysql:///day18","root","abc");
Connection proxy = (Connection) Proxy.newProxyInstance(con.getClass()
.getClassLoader(), con.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
return method.invoke(con, args);
}
});

System.out.println(proxy);
}
}

==================================================================================================
開源連線池

開源組織提供了資料來源的獨立實現:

DBCP 資料庫連線池 C3P0 資料庫連線池Apache Tomcat內建的連線池(apache dbcp 1.dbcp(瞭解)dbcp是apache的一個開源連線池。
要想使用DBCP連線池,要下載jar包
匯入時要匯入兩個
commons-dbcp-1.4.jar
commons-pool-1.5.6.jar
關於dbcp連線池使用
1.手動配置(手動編碼)
BasicDataSource bds = new BasicDataSource();
// 需要設定連線資料庫最基本四個條件
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql:///day18");
bds.setUsername("root");
bds.setPassword("abc");
// 得到一個Connection
Connection con = bds.getConnection();
2.自動配置(使用配置檔案)
Properties props = new Properties();
FileInputStream fis = new FileInputStream("D:\\java1110\\workspace\\day18_2\\src\\dbcp.properties");
props.load(fis);

DataSource ds = BasicDataSourceFactory.createDataSource(props);


package cn.itcast.datasource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;
public class DbcpTest {
// 1.手動配置
@Test
public void test1() throws SQLException {
BasicDataSource bds = new BasicDataSource();
// 需要設定連線資料庫最基本四個條件
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql:///day18");
bds.setUsername("root");
bds.setPassword("abc");
// 得到一個Connection
Connection con = bds.getConnection();
ResultSet rs = con.createStatement().executeQuery(
"select * from account");
while (rs.next()) {
System.out.println(rs.getInt("id") + "  " + rs.getString("name"));
}
rs.close();
con.close(); // 將Connection物件重新裝入到連線池.
}
// 2.自動配置
@Test
public void test2() throws Exception {
Properties props = new Properties();
// props.setProperty("driverClassName", "com.mysql.jdbc.Driver");
// props.setProperty("url", "jdbc:mysql:///day18");
// props.setProperty("username", "root");
// props.setProperty("password", "abc");
FileInputStream fis = new FileInputStream(
"D:\\java1110\\workspace\\day18_2\\src\\dbcp.properties");
props.load(fis);
DataSource ds = BasicDataSourceFactory.createDataSource(props);
// 得到一個Connection
Connection con = ds.getConnection();
ResultSet rs = con.createStatement().executeQuery(
"select * from account");
while (rs.next()) {
System.out.println(rs.getInt("id") + "  " + rs.getString("name"));
}
rs.close();
con.close(); // 將Connection物件重新裝入到連線池.
}
}

2.c3p0(必會)
C3P0是一個開源的JDBC連線池,它實現了資料來源和JNDI繫結,支援JDBC3規範和JDBC2的標準擴充套件。
目前使用它的開源專案有Hibernate,Spring等。
c3p0與dbcp區別
dbcp沒有自動回收空閒連線的功能
c3p0有自動回收空閒連線功能
c3p0連線池使用
1.導包
c3p0-0.9.1.2.jar
使用
1.手動
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql:///day18");
cpds.setUser("root");
cpds.setPassword("abc");
2.自動(使用配置檔案)
c3p0的配置檔案可以是properties也可以是xml.
c3p0的配置檔案如果名稱叫做 c3p0.properties or c3p0-config.xml 並且放置在classpath路徑下(對於web應用就是classes目錄),那麼c3p0會自動查詢。
注意:我們其時只需要將配置檔案放置在src下就可以。
使用:
ComboPooledDataSource cpds = new ComboPooledDataSource();

它會在指定的目錄下查詢指定名稱的配置檔案,並將其中內容載入。


package cn.itcast.datasource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
//c3p0連線池
public class C3p0Test {
@Test
public void test1() throws PropertyVetoException, SQLException {
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql:///day18");
cpds.setUser("root");
cpds.setPassword("abc");
// 得到一個Connection
Connection con = cpds.getConnection();
ResultSet rs = con.createStatement().executeQuery(
"select * from account");
while (rs.next()) {
System.out.println(rs.getInt("id") + "  " + rs.getString("name"));
}
rs.close();
con.close(); // 將Connection物件重新裝入到連線池.
}
@Test
public void test2() throws PropertyVetoException, SQLException {
ComboPooledDataSource cpds = new ComboPooledDataSource();
// 得到一個Connection
Connection con = cpds.getConnection();
ResultSet rs = con.createStatement().executeQuery(
"select * from account");
while (rs.next()) {
System.out.println(rs.getInt("id") + "  " + rs.getString("name"));
}
rs.close();
con.close(); // 將Connection物件重新裝入到連線池.
// String path = this.getClass().getResource("/").getPath();
// System.out.println(path);
}
}

常用基本連線池屬性

   acquireIncrement   如果連線池中連線都被使用了,一次性增長3個新的連線

   initialPoolSize     連線池中初始化連線數量 預設:3

    maxPoolSize      最大連線池中連線數量預設:15連線

   maxIdleTime      如果連線長時間沒有時間,將被回收 預設:0 連線永不過期

   minPoolSize      連線池中最小連線數量 預設:3

c3p0詳細配置:

<c3p0-config>
 <default-config>
<!--當連線池中的連線耗盡的時候c3p0一次同時獲取的連線數。Default: 3 -->
<property name="acquireIncrement">3</property>

<!--定義在從資料庫獲取新連線失敗後重復嘗試的次數。Default: 30 -->
 <property name="acquireRetryAttempts">30</property>

<!--兩次連線中間隔時間,單位毫秒。Default: 1000 -->
 <property name="acquireRetryDelay">1000</property>

<!--連線關閉時預設將所有未提交的操作回滾。Default: false -->
 <property name="autoCommitOnClose">false</property>

<!--c3p0將建一張名為Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個引數那麼
 屬性preferredTestQuery將被忽略。你不能在這張Test表上進行任何操作,它將只供c3p0測試
 使用。Default: null-->
<property name="automaticTestTable">Test</property>

<!--獲取連線失敗將會引起所有等待連線池來獲取連線的執行緒丟擲異常。但是資料來源仍有效
 保留,並在下次呼叫getConnection()的時候繼續嘗試獲取連線。如果設為true,那麼在嘗試
 獲取連線失敗後該資料來源將申明已斷開並永久關閉。Default: false-->
<property name="breakAfterAcquireFailure">false</property>

<!--當連線池用完時客戶端呼叫getConnection()後等待獲取新連線的時間,超時後將丟擲
SQLException,如設為0則無限期等待。單位毫秒。Default: 0 -->
<property name="checkoutTimeout">100</property>

<!--通過實現ConnectionTester或QueryConnectionTester的類來測試連線。類名需制定全路徑。
Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
<property name="connectionTesterClassName"></property>

<!--指定c3p0 libraries的路徑,如果(通常都是這樣)在本地即可獲得那麼無需設定,預設null即可
Default: null-->
<property name="factoryClassLocation">null</property>

<!--Strongly disrecommended. Setting this to true may lead to subtle and bizarre bugs.
(文件原文)作者強烈建議不使用的一個屬性-->
<property name="forceIgnoreUnresolvedTransactions">false</property>

<!--每60秒檢查所有連線池中的空閒連線。Default: 0 -->
<property name="idleConnectionTestPeriod">60</property>

<!--初始化時獲取三個連線,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
<property name="initialPoolSize">3</property>

<!--最大空閒時間,60秒內未使用則連線被丟棄。若為0則永不丟棄。Default: 0 -->
<property name="maxIdleTime">60</property>

<!--連線池中保留的最大連線數。Default: 15 -->
<property name="maxPoolSize">15</property>

<!--JDBC的標準引數,用以控制資料來源內載入的PreparedStatements數量。但由於預快取的statements
屬於單個connection而不是整個連線池。所以設定這個引數需要考慮到多方面的因素。
 如果maxStatements與maxStatementsPerConnection均為0,則快取被關閉。Default: 0-->
<property name="maxStatements">100</property>

<!--maxStatementsPerConnection定義了連線池內單個連線所擁有的最大快取statements數。Default: 0 -->
<property name="maxStatementsPerConnection"></property>

<!--c3p0是非同步操作的,緩慢的JDBC操作通過幫助程序完成。擴充套件這些操作可以有效的提升效能
 通過多執行緒實現多個操作同時被執行。Default: 3-->
 <property name="numHelperThreads">3</property>

<!--當用戶呼叫getConnection()時使root使用者成為去獲取連線的使用者。主要用於連線池連線非c3p0
的資料來源時。Default: null-->
<property name="overrideDefaultUser">root</property>

<!--與overrideDefaultUser引數對應使用的一個引數。Default: null-->
<property name="overrideDefaultPassword">password</property>

<!--密碼。Default: null-->
<property name="password"></property>

<!--定義所有連線測試都執行的測試語句。在使用連線測試的情況下這個一顯著提高測試速度。注意:
 測試的表必須在初始資料來源的時候就存在。Default: null-->
 <property name="preferredTestQuery">select id from test where id=1</property>

<!--使用者修改系統配置引數執行前最多等待300秒。Default: 300 -->
<property name="propertyCycle">300</property>

<!--因效能消耗大請只在需要的時候使用它。如果設為true那麼在每個connection提交的
 時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
等方法來提升連線測試的效能。Default: false -->
<property name="testConnectionOnCheckout">false</property>

<!--如果設為true那麼在取得連線的同時將校驗連線的有效性。Default: false -->
<property name="testConnectionOnCheckin">true</property>

<!--使用者名稱。Default: null-->
<property name="user">root</property>

<!--早期的c3p0版本對JDBC介面採用動態反射代理。在早期版本用途廣泛的情況下這個引數
 允許使用者恢復到動態反射代理以解決不穩定的故障。最新的非反射代理更快並且已經開始
 廣泛的被使用,所以這個引數未必有用。現在原先的動態反射與新的非反射代理同時受到
 支援,但今後可能的版本可能不支援動態反射代理。Default: false-->
<property name="usesTraditionalReflectiveProxies">false</property>

 <property name="automaticTestTable">con_test</property>
 <property name="checkoutTimeout">30000</property>
 <property name="idleConnectionTestPeriod">30</property>
 <property name="initialPoolSize">10</property>
 <property name="maxIdleTime">30</property>
 <property name="maxPoolSize">25</property>
 <property name="minPoolSize">10</property>
 <property name="maxStatements">0</property>
 <user-overrides user="swaldman">
 </user-overrides>
 </default-config>
 <named-config name="dumbTestConfig">
 <property name="maxStatements">200</property>
 <user-overrides user="poop">
 <property name="maxStatements">300</property>
 </user-overrides>
 </named-config>
 </c3p0-config>

tomcat伺服器內建連線池(使用Apache DBCP)

配置tomcat 內建連線池,通過JNDI方式 去訪問tomcat的內建連線池

 JNDI Java命名和目錄介面,是JavaEE一項技術 ,允許將一個Java物件繫結到一個JNDI容器(tomcat)中 ,並且為物件指定一個名稱

通過javax.naming 包 Context 對JNDI 容器中繫結的物件進行查詢,通過指定名稱找到繫結Java對

1、配置使用tomcat 內建連線池 配置<context> 元素

context元素有三種常見配置位置

1) tomcat/conf/context.xml 所有虛擬主機,所有工程都可以訪問該連線池

2)tomcat/conf/Catalina/localhost/context.xml 當前虛擬主機(localhost)下所有工程都可以使用該連線池

3) 當前工程/META-INF/context.xml 只有當前工程可以訪問該連線池

<Context>

 <Resource name="jdbc/EmployeeDB" auth="Container"

           type="javax.sql.DataSource" username="root"password="abc"

           driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql:///day14"

           maxActive="8" maxIdle="4"/>

</Context>

 * 必須先將mysql驅動jar包 複製tomcat/lib下

* 在tomcat啟動伺服器時,建立連線池物件,繫結 jdbc/EmployeeDB 指定名稱上

 2、通過執行在JNDI容器內部的程式(Servlet/JSP)去訪問tomcat內建連線池

Context context = new InitialContext();

Context envCtx =(Context)context.lookup("java:comp/env"); 固定路徑

DataSource datasource = (DataSource)envCtx.lookup("jdbc/EmployeeDB"); 通過繫結名稱,查詢指定java物件

JNDI(JavaNaming and Directory Interface)Java命名和目錄介面,它對應於J2SE中的javax.naming包,這套API的主要作用在於:它可以把Java物件放在一個容器中(支援JNDI容器 Tomcat),併為容器中的java物件取一個名稱,以後程式想獲得Java物件,只需通過名稱檢索即可。其核心APIContext,它代表JNDI容器,其lookup方法為檢索容器中對應名稱的物件。