由於使用jdbc的時候,每操作一次都需要獲取連接(創建),用完之后把連接釋放掉了(銷毀)。所以我們可以通過連接池來優化curd操作。
作用:管理數據庫的連接,提高項目的性能。
思路:就是在連接池初始化的時候存入一定數量的連接,用的時候通過方法獲取,不用的時候歸還連接即可。注意:所有的連接池都必須實現javax.sql.DataSource接口。
獲取連接方法:Connection getConnection()。
歸還連接方法:connection.close(); //注意,這里會使用裝飾者模式,讓連接只是歸還在了連接池中而不是真正的銷毀。
下面我們開始自定義一個自己的連接池。
1.初始化連接池。
首先我們需要定義一個泛型為Connection的List集合,用來存放連接。在這里我們初始容量定義3個。
然后通過JDBCUtills類來獲取連接,並將連接放入List集合中。(關於JDBCUtils工具類的封裝,見如下鏈接)
public class MyDataSource { //初始化只需要一次就好,所以放在static靜態代碼塊中 static LinkedList<Connection> list = new LinkedList<>(); static{ //static不能拋異常,只能try...catch try { for(int i = 0; i < 3; i++){ Connection conn = JDBCUtils_plus.getConnection(); list.add(conn); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
2.從連接池中獲取連接
先判斷連接池是否為空,若為空,通過循環,創建新的連接放入連接池。若不為空,從List集合中取出第一個連接(removeFirst),然后j將連接返回。
不過,這里通過裝飾者模式包裝了一個ConnectionWrapper類,包裝類的作用是包裝了Connection的close()方法,使得它不會銷毀連接,只是將連接放回。
關於ConnectionWrapper類的創建,后面會詳述。
public static Connection getConnection(){ if(list.isEmpty()){ //如果為空,再獲取連接進去 for(int i = 0; i < 3;i++){ try { list.add(JDBCUtils_plus.getConnection()); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } Connection conn = list.removeFirst(); System.out.println("獲取連接池"); //通過構造方法將connection類傳入包裝類中 ConnectionWrapper cw = new ConnectionWrapper(conn,list); //返回包裝類 return cw; }
}
3.利用裝飾者模式,創建Connection的包裝類ConnectionWrapper
裝飾者模式的具體介紹參照裝飾者模式
裝飾者模式中,裝飾者和被裝飾者需要繼承同一個類或者實現同一個接口。由於Connection本身就是一個接口,所以很自然的讓裝飾者繼承這個接口。
通過查看Connection接口的api發現,接口中的抽象方法實在是太多了。如果我們直接讓裝飾者類繼承,需要重寫太多方法。
以下為部分方法:
所以在這里,我們需要使用適配器模式,新增加一個類ConnectionAdapter,讓它來實現Connection接口,讓裝飾者類繼承ConnectionAdapter類,這樣就可以只重寫需要用的方法啦。
該類部分截圖:
裝飾者類:
public class ConnectionWrapper extends ConnectionAdapter { private Connection conn; private LinkedList<Connection> list; public ConnectionWrapper() { }
//通過構造方法將被裝飾者類傳入進來並賦值給內部類。將List集合也傳進來,因為有歸還連接的操作。 public ConnectionWrapper(Connection conn,LinkedList<Connection> list) { this.conn = conn; this.list = list; } @Override
//重寫close方法,歸還連接。 public void close() throws SQLException { System.out.println("前"+list.size()); list.addLast(this); System.out.println("后"+list.size()); }
//以下兩個方法就調用以前的方法就行 @Override public Statement createStatement() throws SQLException { // TODO Auto-generated method stub return conn.createStatement(); } @Override public PreparedStatement prepareStatement(String sql) throws SQLException { // TODO Auto-generated method stub return conn.prepareStatement(sql); }
測試類:
public class Test { public static void main(String[] args) throws SQLException { Connection conn = MyDataSource.getConnection(); //注意:這里的conn已經不是單純的Connection了,它是實際傳過來的裝飾類ConnectionWrapper String sql = "select * from student"; PreparedStatement st = conn.prepareStatement(sql); ResultSet rs = st.executeQuery(); while(rs.next()){ System.out.println( rs.getString("id")+" "+rs.getString("name") +" "+rs.getString("score")); } //這里調用的也不是Connection以前的close()方法了,這里是包裝類的增強close()方法。不銷毀連接,只歸還連接。 conn.close(); } }
運行結果如下:
以上,就是一個簡易的jdbc連接池。
然而現實有兩個比較常用的,封裝好的連接池,他們分別是DBCP和C3P0,以下介紹他們的用法:
一.DBCP連接池
apache組織出品。
1.導入jar包(commons-dbcp-1.4.jar和commons-pool-1.5.6.jar)
(emmmm請無視中間的dbutils的jar包......)
2.使用
a.硬編碼(不推薦使用)
以下是硬編碼方式的配置信息:
//創建連接池 BasicDataSource ds = new BasicDataSource(); //配置信息 ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql:///exercise"); ds.setUsername("root"); ds.setPassword("123456");
b.采用配置文件的方式讀取配置信息。這里拿properties文件舉例。(名稱為dbcp.properties)
代碼如下:
//存放配置文件 Properties prop = new Properties(); prop.load(new FileInputStrea("src/dbcp.properties")) //創建連接池 DataSource ds = new BasicDataSourceFactory().createDataSource(prop);
Connection conn=ds.getConnection();
二.C3P0連接池(比較常見)
hibernate和spring使用,有自動回收空閑連接的功能。
1.導入jar包(c3p0-0.9.1.2.jar)
2.使用
a.硬編碼:
ComboPooledDataSource ds = new ComboPooledDataSource(); //設置基本參數 ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql:///exercise"); ds.setUser("root"); ds.setPassword("123456"); Connection conn=ds.getConnection();
b.讀取配置文件
注意:配置文件的名稱:c3p0.properties 或者 c3p0-config.xml時,可以直接使用new ComboPooledDataSource()來獲取配置文件(默認配置)。
new ComboPooledDataSource(String configName)//使用命名的配置 若配置的名字找不到,使用默認的配置
以下用properties文件舉例:
代碼如下:
//由於配置文件寫的默認名字,這里使用無參構造 ComboPooledDataSource ds =new ComboPooledDataSource(); Connection conn=ds.getConnection();