自定義一個(gè)簡單的JDBC連接池實(shí)現(xiàn)方法
一、什么是JDBC連接池?
在傳統(tǒng)的JDBC連接中,每次獲得一個(gè)Connection連接都需要加載通過一些繁雜的代碼去獲取,例如以下代碼:
public static Connection getConn(){ Connection conn = null; String url = "jdbc:mysql://localhost:3306/test"; String user = "root"; String password = "root"; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(url, user, password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return conn; }
這樣繁雜的操作只為了獲取一次連接,當(dāng)然,我們可以將其封裝成一個(gè)工具類來訪問(上圖以封裝好Connection的連接),但是每一次連接都需要取加載一次是不是很浪費(fèi)性能,為了優(yōu)化性能,那么就出現(xiàn)了連接池。
連接池在初始化的時(shí)候就創(chuàng)建了幾個(gè)連接供我們使用,當(dāng)我們需要連接時(shí)只需要從連接池中獲取已存在的連接,當(dāng)初始化的幾個(gè)連接都沒有時(shí),會(huì)重新創(chuàng)建一個(gè)連接,使用完連接后不會(huì)去銷毀連接,而是歸還給連接池供后面需要連接的使用。(當(dāng)然,連接池不僅僅只是這么簡單,這里就只做這些介紹)
常用的連接池有DBCP、C3P0,現(xiàn)在最主流是好像是阿里的Druid連接池,還有tomcat的自帶的JNDI連接池
二、自定義一個(gè)簡單的連接池
對自定義連接池的分析:
1.2.因?yàn)槭沁B接池 ,我們需要實(shí)現(xiàn)DataSource接口,并實(shí)現(xiàn)其中的方法,基于我們的情況,我們關(guān)于與getConnection()方法;
2.既然要存放幾個(gè)連接對象,那么我們用一個(gè)集合來存放它,基于會(huì)經(jīng)常操作增加和刪除那么選用LinkedList;
3.連接的銷毀并不是銷毀連接,而是將連接歸還給連接池
編碼:
1.創(chuàng)建一個(gè)類MyDataSource 并實(shí)現(xiàn)DataSource接口
當(dāng)此類加載時(shí)它就需要有一個(gè)容器來存放Connection,所以定義一個(gè)靜態(tài)的屬性:
private static List<Connection> connectionList = new LinkedList<>();
2.因?yàn)樾枰~@得數(shù)據(jù)庫連接,所以我們封裝一個(gè)獲取數(shù)據(jù)庫連接的方法
public Connection getOneConnection(){ Connection conn = null; try{ //此處通過外部的properties文件來獲取的數(shù)據(jù),這樣更加靈活。 InputStream in = MyDataSource.class.getClassLoader(). getResourceAsStream("jdbc/jdbc.properties"); Properties pro = new Properties(); pro.load(in); driver = pro.getProperty("driver"); url = pro.getProperty("url"); username = pro.getProperty("user"); password = pro.getProperty("password"); Class.forName(driver); conn = DriverManager.getConnection(url,username,password); }catch (Exception e){ e.getStackTrace(); } return conn; }
注意的是我這里通過propertie文件的獲取的數(shù)據(jù),可根據(jù)實(shí)際情況來選擇
3.初始化幾個(gè)連接放入容器中??梢允褂渺o態(tài)代碼塊來實(shí)現(xiàn),但是如果沒有使用此數(shù)據(jù)源那么就造成了資源的浪費(fèi),所以我考慮將初始化幾個(gè)連接的實(shí)現(xiàn)放到他的構(gòu)造方法中,即當(dāng)需要此連接池的時(shí)候他才會(huì)隨之創(chuàng)建幾個(gè)連接。如下:
public MyDataSource() { for (int i = 0; i < 5; i++) { Connection conn = getOneConnection();//調(diào)用創(chuàng)建連接的方法 connectionList.add(conn); } }
4.現(xiàn)在開始重寫外部從此連接池中獲取連接的方法getConnection()
@Override public Connection getConnection() throws SQLException { Connection conn = null; if(connectionList == null || connectionList.size() <= 0){ Connection connection = getConnection(); connectionList.add(connection); } conn = connectionList.remove(0); return conn; }
5.創(chuàng)建一個(gè)對象返回的方法,即將用完的連接放入歸還到連接池中
public void backConnection(Connection conn){ connectionList.add(conn); }
OK,這樣就完成了一個(gè)簡單的自定義連接池,測試代碼如下:
public static void main( String[] args ) throws SQLException { MyDataSource dataSource = new MyDataSource(); Connection conn = dataSource.getConnection(); String sql = "select * from user where u_id = ?"; PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(sql); ps.setInt(1, 1); rs = ps.executeQuery(); while (rs.next()) { System.out.println("id="+rs.getInt(1)); System.out.println("username="+rs.getString(2)); System.out.println("password="+rs.getString(3)); } } catch (SQLException e) { e.printStackTrace(); } finally { dataSource.backConnection(conn); } }
因?yàn)楹雎?,我的代碼中沒有關(guān)閉其他兩個(gè)對象。
現(xiàn)在有一個(gè)小問題就是,我們的關(guān)閉連接是通過連接池的方法來實(shí)現(xiàn)的,但是,如果用戶調(diào)用Connection對象的close方法,那么連接時(shí)被銷毀了,并沒有返回給連接池,那么我們來優(yōu)化它,讓用戶使用close()方法不會(huì)去銷毀連接,而是去歸還連接。
方案有很多中,這里采用裝飾著模式的一種。
優(yōu)化:
1.新建一個(gè)類MyConnection來實(shí)現(xiàn)Connection接口,其中他有屬性類型為Connection conn和一個(gè)Liis<Connection>。
private Connection conn; private List<Connection> pool; public MyConnection(Connection conn, List<Connection> pool) { this.conn = conn; this.pool = pool; }
2.然后實(shí)現(xiàn)接口的close方法
@Override public void close() throws SQLException { System.out.println("回收連接"); pool.add(conn); }
3.然后實(shí)現(xiàn)他獲取Statement的方法,如果不實(shí)現(xiàn)那么獲取此Statement會(huì)出現(xiàn)空指針錯(cuò)誤,我這里就只實(shí)現(xiàn)了PreparedStatement的獲取方法
@Override public PreparedStatement prepareStatement(String sql) throws SQLException { System.out.println("獲得Statement"); return conn.prepareStatement(sql); }
4.然后刪除掉MyDataSource類中歸還連接的方法backConnection,并將構(gòu)造方法和獲取連接的方法做如下改造
public MyDataSource2() { for (int i = 0; i < 5; i++) { Connection conn = getOneConnection(); MyConnection myConn = new MyConnection(conn, connectionList); connectionList.add(myConn); } }
@Override public Connection getConnection() throws SQLException { Connection conn = null; if(connectionList == null || connectionList.size() <= 0){ Connection connection = getConnection(); MyConnection myConn = new MyConnection(connection, connectionList); connectionList.add(myConn); } conn = connectionList.remove(0); return conn; }
好了,這樣用戶直接調(diào)用我們的Connection的close方法就不會(huì)去銷毀連接了,會(huì)正確的歸還給了連接池了,對測試代碼稍做修改即可測試。
以上這篇自定義一個(gè)簡單的JDBC連接池實(shí)現(xiàn)方法就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java Apache Shiro安全框架快速開發(fā)詳解流程
Apache Shiro是一個(gè)強(qiáng)大且易用的Java安全框架,執(zhí)行身份驗(yàn)證、授權(quán)、密碼和會(huì)話管理。使用Shiro的易于理解的API,您可以快速、輕松地獲得任何應(yīng)用程序,從最小的移動(dòng)應(yīng)用程序到最大的網(wǎng)絡(luò)和企業(yè)應(yīng)用程序2021-10-10Nacos docker單機(jī)模式部署實(shí)現(xiàn)過程詳解
這篇文章主要介紹了Nacos docker單機(jī)模式部署實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09java 實(shí)現(xiàn)約瑟夫環(huán)的實(shí)例代碼
這一次是借鑒模仿別人寫的代碼,以前覺得不好將數(shù)據(jù)結(jié)構(gòu)的鏈結(jié)構(gòu)什么的遷移到j(luò)ava上來使用,但這一次確實(shí)讓我感受到了可以自己構(gòu)造數(shù)據(jù)結(jié)構(gòu),然后使用類似鏈的方式來解決約瑟夫環(huán),有所頓悟。不多說,繼續(xù)上代碼2013-10-10java.lang.UnsatisfiedLinkError: %1 不是有效的Win32應(yīng)用程序錯(cuò)誤解決
這篇文章主要給大家介紹了關(guān)于java.lang.UnsatisfiedLinkError: %1 不是有效的Win32應(yīng)用程序錯(cuò)誤的解決方法,文中介紹的非常詳細(xì),需要的朋友們可以參考學(xué)習(xí),下面來一起看看吧。2017-03-03SpringCloud中的斷路器(Hystrix)和斷路器監(jiān)控(Dashboard)
本篇主要介紹的是SpringCloud中的斷路器(Hystrix)和斷路器指標(biāo)看板(Dashboard)的相關(guān)使用知識(shí),需要的朋友可以參考下2019-06-06