1. 程式人生 > >初識數據庫連接池開源框架Druid

初識數據庫連接池開源框架Druid

數據庫連接

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 ylf16  *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      * @return35      */36     public static synchronized DBPoolConnection getInstance(){37         if (null == dbPoolConnection){38             dbPoolConnection = new DBPoolConnection();39         }40         return dbPoolConnection;41     }42 43     /**44      * 返回druid數據庫連接45      * @return46      * @throws SQLException47      */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 ylf15  *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 ylf10  *11  * 2016年10月21日12  */13 public class Client {14 15     /**16      * @param args17      */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/DruidTestusername=root
password=0000filters=stat
initialSize=2maxActive=300maxWait=60000timeBetweenEvictionRunsMillis=60000minEvictableIdleTimeMillis=300000validationQuery=SELECT 1testWhileIdle=truetestOnBorrow=falsetestOnReturn=falsepoolPreparedStatements=falsemaxPoolPreparedStatementPerConnectionSize=200

技術分享


初識數據庫連接池開源框架Druid