Druid是阿里巴巴的一個數據庫連接池開源框架,准確來說它不僅僅包括數據庫連接池這么簡單,它還提供強大的監控和擴展功能。本文僅僅是在不采用Spring框架對Druid的窺探,采用目前最新版本druid1.0.26 github地址:https://github.com/alibaba/druid。
在開始之前還是再說說為什么不配套使用Spring來使用Druid連接池,原因其實很簡單,在Spring框架的配置文件中僅僅一個配置datasource就可以使用Druid了。那到底配置這個datasource數據源時Spring到底對它做了什么呢?它到底是怎么來實現這個datasource數據源的呢?如果不知其二只知其一,那才真是只是個搬磚的。
下面我們正式開始吧,首先還是一覽工程包結構。
同樣有兩個jar需要引入,一是druid,二是mysql-connector。
我們首先實現util包里的DBPoolConnection類,這個類用來創建數據庫連接池單例以及返回一個數據庫連接。為什么數據庫連接池需要單例呢?原因其實很簡單,我們可以想象在一個web應用中,同時可能會存在多個請求如果為每一個請求都創建一個數據庫連接池,那還有什么意義呢?應該是不論有多少個並發請求,都應該只存在一個數據庫連接池,在這個數據庫連接池中為每個請求創建一個數據庫連接。
1 package util; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.InputStream; 6 import java.sql.SQLException; 7 import java.util.Properties; 8 9 import com.alibaba.druid.pool.DruidDataSource; 10 import com.alibaba.druid.pool.DruidDataSourceFactory; 11 import com.alibaba.druid.pool.DruidPooledConnection; 12 13 /** 14 * 要實現單例模式,保證全局只有一個數據庫連接池 15 * @author ylf 16 * 17 * 2016年10月21日 18 */ 19 public class DBPoolConnection { 20 private static DBPoolConnection dbPoolConnection = null; 21 private static DruidDataSource druidDataSource = null; 22 23 static { 24 Properties properties = loadPropertiesFile("db_server.properties"); 25 try { 26 druidDataSource = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties); //DruidDataSrouce工廠模式 27 } catch (Exception e) { 28 e.printStackTrace(); 29 } 30 } 31 32 /** 33 * 數據庫連接池單例 34 * @return 35 */ 36 public static synchronized DBPoolConnection getInstance(){ 37 if (null == dbPoolConnection){ 38 dbPoolConnection = new DBPoolConnection(); 39 } 40 return dbPoolConnection; 41 } 42 43 /** 44 * 返回druid數據庫連接 45 * @return 46 * @throws SQLException 47 */ 48 public DruidPooledConnection getConnection() throws SQLException{ 49 return druidDataSource.getConnection(); 50 } 51 /** 52 * @param string 配置文件名 53 * @return Properties對象 54 */ 55 private static Properties loadPropertiesFile(String fullFile) { 56 String webRootPath = null; 57 if (null == fullFile || fullFile.equals("")){ 58 throw new IllegalArgumentException("Properties file path can not be null" + fullFile); 59 } 60 webRootPath = DBPoolConnection.class.getClassLoader().getResource("").getPath(); 61 webRootPath = new File(webRootPath).getParent(); 62 InputStream inputStream = null; 63 Properties p =null; 64 try { 65 inputStream = new FileInputStream(new File(webRootPath + File.separator + fullFile)); 66 p = new Properties(); 67 p.load(inputStream); 68 } catch (Exception e) { 69 e.printStackTrace(); 70 } finally { 71 try { 72 if (null != inputStream){ 73 inputStream.close(); 74 } 75 } catch (Exception e) { 76 e.printStackTrace(); 77 } 78 } 79 80 return p; 81 } 82 83 }
第26行代碼實例化一個DruidDataSource時,我們可以通過Druid框架為我們提供的DruidDataSourceFactory創建出一個DruidDataSource實例,工廠模式給我們提供了大大的便利。
第36行getInstance方法為我們創建出一個數據庫連接池實例,這里即用到了單例模式,在這個地方我們可以使用synchronized方法來對getInstance加鎖(懶加載)實現線程安全,同時我們也可以使用勤加載來實現線程安全即去掉synchronized關鍵字,刪掉37-39行代碼,將第20行代碼修改為private static DBPoolConnection dbPoolConnection = new DBPoolConnection()。這兩種方式各有其優缺點,懶加載好處就是“用到才實例化”,缺點就是“synchronized關鍵字對方法加鎖的粒度稍稍有點大,采用同步的方式實現線程安全會帶來額外的開銷”,而勤加載的好處就是“不使用同步的方式實現線程安全,省去了同步機制帶來的額外開銷”,缺點即是“未用到也會實例化”。至於怎么選擇,根據實際情況。這里是之前對單例模式的兩篇博文,《單例模式》、《再說單例模式的線程安全問題》。
第55行代碼loadPropertiesFile方法是對properties配置文件的加載。
我們在這個類所做的工作差不多就是在spring配置文件中的那一句<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">。很簡單的一句話,這就是為什么我不想使用Spring框架來配合使用Druid,因為這只會造成只知其一不知其二的結果。
我們接在來實現dao包中的DruidDao類,開始對數據進行持久化操作。由於我們沒有用到MyBatis等持久層框架,所以我們不得不使用JDBC來操作數據庫,雖然麻煩一點,但這是所有所有框架的基礎。
1 /** 2 * 3 */ 4 package dao; 5 6 import java.sql.PreparedStatement; 7 import java.sql.SQLException; 8 9 import com.alibaba.druid.pool.DruidPooledConnection; 10 11 import util.DBPoolConnection; 12 13 /** 14 * @author ylf 15 * 16 * 2016年10月21日 17 */ 18 public class DruidDao { 19 20 public void insert(String sql){ 21 DBPoolConnection dbp = DBPoolConnection.getInstance(); //獲取數據連接池單例 22 DruidPooledConnection conn = null; 23 PreparedStatement ps = null; 24 try { 25 conn = dbp.getConnection(); //從數據庫連接池中獲取數據庫連接 26 ps = conn.prepareStatement(sql); 27 ps.executeUpdate(); 28 } catch (SQLException e) { 29 e.printStackTrace(); 30 } finally { 31 try { 32 if (null != ps){ 33 ps.close(); 34 } 35 if (null != conn){ 36 conn.close(); 37 } 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 } 42 } 43 }
我們只對數據做插入操作。下面我們測試一下,各個屬性的含義可參考:https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8
1 /** 2 * 3 */ 4 package test; 5 6 import dao.DruidDao; 7 8 /** 9 * @author ylf 10 * 11 * 2016年10月21日 12 */ 13 public class Client { 14 15 /** 16 * @param args 17 */ 18 public static void main(String[] args) { 19 DruidDao druidDao = new DruidDao(); 20 String sql = "insert into test (name) values(\"keven\")"; 21 druidDao.insert(sql); 22 } 23 24 }
查看數據庫插入成功。
另外db_server.properties數據庫的配置文件如下:
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/DruidTest username=root password=0000 filters=stat initialSize=2 maxActive=300 maxWait=60000 timeBetweenEvictionRunsMillis=60000 minEvictableIdleTimeMillis=300000 validationQuery=SELECT 1 testWhileIdle=true testOnBorrow=false testOnReturn=false poolPreparedStatements=false maxPoolPreparedStatementPerConnectionSize=200