JDBC數據庫連接池常用的三種實現方式

JDBC概念原理

JDBC(Java Data Base Connectivity,java數據庫連接)是一種用於執行SQL語句的Java API,可以爲多種關係數據庫提供統一訪問,它由一組用Java語言編寫的類和接口組成。是Java訪問數據庫的標準規範。

  • JDBC提供了一種基準,據此可以構建更高級的工具和接口,使數據庫開發人員能夠編寫數據庫應用程序。
  • JDBC需要連接驅動,驅動是兩個設備要進行通信,滿足一定通信數據格式,數據格式由設備提供商規定,
    設備提供商爲設備提供驅動軟件,通過軟件可以與該設備進行通信。

JDBC原理:Java提供訪問數據庫規範稱爲JDBC,而生產廠商提供規範的實現類稱爲驅動。JDBC是一套API接口(Sun公司定義的類或接口),驅動是接口的實現,沒有驅動將無法完成數據庫連接,從而不能操作數據庫!每個數據庫廠商都需要提供自己的驅動,用來連接自己公司的數據庫,也就是說驅動一般都由數據庫生成廠商提供。

總結

  • JDBC是java提供給開發人員的一套操作數據庫的接口
  • 數據庫驅動就是實現該接口的實現類 

mysql建數據庫、創建表

CREATE TABLE `user` (
  `userid` INT(11) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(255) DEFAULT NULL,
  `password` VARCHAR(255) DEFAULT NULL,
  `email` VARCHAR(255) DEFAULT NULL,
  `phone` VARCHAR(255) DEFAULT NULL,
  `status` VARCHAR(255) NOT NULL DEFAULT '0',
  `code` VARCHAR(255) DEFAULT NULL,
  PRIMARY KEY (`userid`)
) ENGINE=INNODB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;


 #創建數據庫
  create database mybase;
  #使用數據庫
  use mybase;
  ###創建分類表
  create table sort(
    sid int PRIMARY KEY AUTO_INCREMENT,
    sname varchar(100),
    sprice DOUBLE,
    sdesc VARCHAR(500)
  );

案例代碼

//JDBCUtils工具類代碼
  public class JDBCUtils {
      private JDBCUtils(){}
      private static Connection con ;
      
      static{
          try{
              Class.forName("com.mysql.jdbc.Driver");
              String url = "jdbc:mysql://localhost:3306/test";
              String username="root";
              String password="123";
              con = DriverManager.getConnection(url, username, password);
          }catch(Exception ex){
              throw new RuntimeException(ex+"數據庫連接失敗");
          }
      }
      
      /*
       * 定義靜態方法,返回數據庫的連接對象
       */
      public static Connection getConnection(){
          return con;
      }
      
      
      public static void close(Connection con,Statement stat){
           
           if(stat!=null){
               try{
                   stat.close();
               }catch(SQLException ex){}
           }
           
           if(con!=null){
               try{
                   con.close();
               }catch(SQLException ex){}
           }
           
      }
      
      
      public static void close(Connection con,Statement stat , ResultSet rs){
           if(rs!=null){
               try{
                   rs.close();
               }catch(SQLException ex){}
           }
           
           if(stat!=null){
               try{
                   stat.close();
               }catch(SQLException ex){}
           }
           
           if(con!=null){
               try{
                   con.close();
               }catch(SQLException ex){}
           }
           
      }
  }
//測試JDBCUtils工具類的代碼
public class TestJDBCUtils {
  public static void main(String[] args)throws Exception {
      Connection con = JDBCUtils.getConnection();
      PreparedStatement pst = con.prepareStatement("SELECT sname FROM user");
      ResultSet rs = pst.executeQuery();
      while(rs.next()){
          System.out.println(rs.getString("sname"));
      }
      JDBCUtils.close(con, pst, rs);
  }

 

數據表數據存儲對象

  //定義實體類Sort
  public class Sort {
  private int sid;
  private String sname;
  private double sprice;
  private String sdesc;
  public Sort(int sid, String sname, double sprice, String sdesc) {
      this.sid = sid;
      this.sname = sname;
      this.sprice = sprice;
      this.sdesc = sdesc;
  }
  public Sort(){}
  public int getSid() {
      return sid;
  }
  public void setSid(int sid) {
      this.sid = sid;
  }
  public String getSname() {
      return sname;
  }
  public void setSname(String sname) {
      this.sname = sname;
  }
  public double getSprice() {
      return sprice;
  }
  public void setSprice(double sprice) {
      this.sprice = sprice;
  }
  public String getSdesc() {
      return sdesc;
  }
  public void setSdesc(String sdesc) {
      this.sdesc = sdesc;
  }
  @Override
  public String toString() {
      return "Sort [sid=" + sid + ", sname=" + sname + ", sprice=" + sprice + ", sdesc=" + sdesc + "]";
  }               
}

/*
 *  JDBC讀取數據表sort,每行數據封裝到Sort類的對象中
 *  很多個Sort類對象,存儲到List集合中
 */
public class JDBCDemo {
  public static void main(String[] args) throws Exception{
      //使用JDBC工具類,直接獲取數據庫連接對象
      Connection con = JDBCUtils.getConnection();
      //連接獲取數據庫SQL語句執行者對象
      PreparedStatement pst = con.prepareStatement("SELECT * FROM sort");
      //調用查詢方法,獲取結果集
      ResultSet rs = pst.executeQuery();
      //創建集合對象
      List<Sort> list = new ArrayList<Sort>();
      while(rs.next()){
          //獲取到每個列數據,封裝到Sort對象中
          Sort s = new Sort(rs.getInt("sid"),rs.getString("sname"),rs.getDouble("sprice"),rs.getString("sdesc"));
          //封裝的Sort對象,存儲到集合中
          list.add(s);
      }
      JDBCUtils.close(con, pst, rs);
      //遍歷List集合
      for(Sort s : list){
          System.out.println(s);
      }
  }
}

 

也可以通過配置文件讀取配置

src路徑下建立database.properties

  driverClass=com.mysql.jdbc.Driver
  url=jdbc:mysql://localhost:3306/mybase
  username=root
  password=123  

 

 讀取配置文件並建立數據庫連接的工具類

  /*
   *  編寫數據庫連接的工具類,JDBC工具類
   *  獲取連接對象採用讀取配置文件方式
   *  讀取文件獲取連接,執行一次,static{}
   */
  public class JDBCUtilsConfig {
      private static Connection con ;
      private static String driverClass;
      private static String url;
      private static String username;
      private static String password;
      
      static{
          try{
              readConfig();
              Class.forName(driverClass);
              con = DriverManager.getConnection(url, username, password);
          }catch(Exception ex){
              throw new RuntimeException("數據庫連接失敗");
          }
      }
      
      private static void readConfig()throws Exception{
          InputStream in = JDBCUtilsConfig.class.getClassLoader().getResourceAsStream("database.properties");
           Properties pro = new Properties();
           pro.load(in);
           driverClass=pro.getProperty("driverClass");
           url = pro.getProperty("url");
           username = pro.getProperty("username");
           password = pro.getProperty("password");
      }
      
      
      public static Connection getConnection(){
          return con;
      }
      
  }  

 

 

連接池的使用

數據庫連接是一種關鍵的有限的昂貴的資源,這一點在多用戶的網頁應用程序中體現得尤爲突出。  一個數據庫連接對象均對應一個物理數據庫連接,每次操作都打開一個物理連接,使用完都關閉連接,這樣造成系統的 性能低下。
數據庫連接池的解決方案是在應用程序啓動時建立足夠的數據庫連接,並講這些連接組成一個連接池,由應用程序動態地對池中的連接進行申請、使用和釋放。
對於多於連接池中連接數的併發請求,應該在請求隊列中排隊等待。並且應用程序可以根據池中連接的使用率,動態增加或減少池中的連接數。
 連接池技術儘可能多地重用了消耗內存地資源,大大節省了內存,提高了服務器地服務效率,能夠支持更多的客戶服務。通過使用連接池,將大大提高程序運行效率,同時,我們可以通過其自身的管理機制來監視數據庫連接的數量、使用情況等。

數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重複使用一個現有的數據庫連接,而不是再重新建立一個。

連接池的工作原理

連接池的工作原理主要由三部分組成,分別爲連接池的建立、連接池中連接的使用管理、連接池的關閉。
第一、連接池的建立。一般在系統初始化時,連接池會根據系統配置建立,並在池中創建了幾個連接對象,以便使用時能從連接池中獲取。連接池中的連接不能隨意創建和關閉,這樣避免了連接隨意建立和關閉造成的系統開銷。Java中提供了很多容器類可以方便的構建連接池,例如Vector、Stack等。
第二、連接池的管理。連接池管理策略是連接池機制的核心,連接池內連接的分配和釋放對系統的性能有很大的影響。其管理策略是: 當客戶請求數據庫連接時,首先查看連接池中是否有空閒連接,如果存在空閒連接,則將連接分配給客戶使用;如果沒有空閒連接,則查看當前所開的連接數是否已經達到最大連接數,如果沒達到就重新創建一個連接給請求的客戶;如果達到就按設定的最大等待時間進行等待,如果超出最大等待時間,則拋出異常給客戶。
當客戶釋放數據庫連接時,先判斷該連接的引用次數是否超過了規定值,如果超過就從連接池中刪除該連接,否則保留爲其他客戶服務。
該策略保證了數據庫連接的有效複用,避免頻繁的建立、釋放連接所帶來的系統資源開銷。
 第三、連接池的關閉。當應用程序退出時,關閉連接池中所有的連接,釋放連接池相關的資源,該過程正好與創建相反。

傳統數據庫連接對比數據庫連接池

æ°æ®åºå¸¸ç¨è¿æ¥æ± æ»ç»

不使用數據庫連接池的步驟:
TCP建立連接的三次握手
MySQL認證的三次握手
真正的SQL執行
MySQL的關閉
TCP的四次握手關閉
可以看到,爲了執行一條SQL,卻多了非常多我們不關心的網絡交互。

優點:實現簡單
缺點:

  1. 網絡IO較多
  2. 數據庫的負載較高
  3. 響應時間較長及QPS較低
  4. 應用頻繁的創建連接和關閉連接,導致臨時對象較多,GC頻繁
  5. 在關閉連接後,會出現大量TIME_WAIT 的TCP狀態(在2個MSL之後關閉)

æ°æ®åºå¸¸ç¨è¿æ¥æ± æ»ç»

使用數據庫連接池的步驟:

第一次訪問的時候,需要建立連接。 但是之後的訪問,均會複用之前創建的連接,直接執行SQL語句。

優點:

較少了網絡開銷
系統的性能會有一個實質的提升
沒了麻煩的TIME_WAIT狀態

目前存在多個開源的java數據庫連接池,這些連接池都是在java.sql基礎上編寫而成。

常用連接池

DBCP連接池
DBCP 是一個開源的連接池,是Apache Common成員之一,在企業開發中也比較常見,tomcat內置的連接池。該數據庫連接池既可以與應用服務器整合使用,也可由應用程序獨立使用。

應用程序應在系統中增加如下兩個 jar 文件:

導入jar包

  • mysql-connector-java-5.1.37-bin.jar:數據庫驅動
  • commons-dbutils-1.6.jar:提供QueryRunner類方便進行增刪改查操作
  • commons-dbcp-1.4.jar:連接池的實現
  • commons-pool-1.5.6.jar:連接池實現的依賴庫,提供高效的數據庫連接池技術

核心類:org.apache.commons.dbcp2.BasicDataSource extends Object implements DataSource 

案例代碼

實現數據庫連接池工具類

/*
   *  使用DBCP實現數據庫的連接池
   *  連接池配置,自定義類,
   *  最基本四項完整
   *  對於數據庫連接池其他配置,自定義
   */

  import javax.sql.DataSource;

  import org.apache.commons.dbcp.BasicDataSource;
  public class JDBCUtils{
      //創建出BasicDataSource類對象
      private static BasicDataSource datasource = new BasicDataSource();
      
      //靜態代碼塊,對象BasicDataSource對象中的配置,自定義
      static{
          //數據庫連接信息,必須的
          datasource.setDriverClassName("com.mysql.jdbc.Driver");
          datasource.setUrl("jdbc:mysql://localhost:3306/user");
          datasource.setUsername("root");
          datasource.setPassword("123");
          //對象連接池中的連接數量配置,可選的
          datasource.setInitialSize(10);//初始化的連接數
          datasource.setMaxActive(8);//最大連接數量
          datasource.setMaxIdle(5);//最大空閒數
          datasource.setMinIdle(1);//最小空閒
      }
      
      
      //定義靜態方法,返回BasicDataSource類的對象
      public static DataSource getDataSource(){
          return datasource;
      }
  }

 

工具類的測試

/*
   *  測試寫好的工具類,
   *  提供的是一個DataSource接口的數據源
   *  QueryRunner類構造方法,接收DataSource接口的實現類
   *  後面,調用方法update,query,無需傳遞他們Connection連接對象
   */

  import java.sql.SQLException;
  import java.util.List;

  import org.apache.commons.dbutils.QueryRunner;
  import org.apache.commons.dbutils.handlers.ArrayListHandler;

  import cn.itcast.jdbcutils.JDBCUtils;
  public class QueryRunnerDemo{
      public static void main(String[] args) {
          select();
      }
      //定義2個方法,實現數據表的添加,數據表查詢
      //QueryRunner類對象,寫在類成員位置
      private static QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource()); 
      
      //數據表查詢
      public static void select(){
          String sql = "SELECT * FROM user";
          try{
          List<Object[]> list = qr.query(sql, new ArrayListHandler());
          for(Object[] objs : list){
              for(Object obj : objs){
                  System.out.print(obj+"\t");
              }
              System.out.println();
          }
          }catch(SQLException ex){
              throw new RuntimeException("數據查詢失敗");
          }
      }
      
      //數據表添加數據
      public static void insert(){
          String sql = "INSERT INTO user(username,password,email,phone,status,code)VALUES(?,?,?,?,?,?)";
          Object[] params = {"AAA",123456,"[email protected]","150000000","0","234"};
          try{
              int row = qr.update(sql, params);
              System.out.println(row);
          }catch(SQLException ex){
              throw new RuntimeException("數據添加失敗");
          }
      }
      
  }

 

 C3P0連接池
最常用的連接池技術!Spring框架默認支持C3P0連接池技術!

導入jar包

  • mysql-connector-java-5.1.37-bin.jar:數據庫驅動
  • commons-dbutils-1.6.jar:提供QueryRunner類方便進行增刪改查操作
  • c3p0-0.9.1.2.jar:連接池的實現

核心類:com.mchange.v2.c3p0.ComboPooledDataSource implements PooledDataSource extends DataSource

案例代碼:

package com.test.common.util;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

public class DataSourceFactory
{
    private static Map<String, ComboPooledDataSource> dsMap = new HashMap();

    public static synchronized ComboPooledDataSource getDataSource() {
        return getDataSource("default");
    }

    public static synchronized ComboPooledDataSource getDataSource(String dbName) {
        ComboPooledDataSource ds = (ComboPooledDataSource)dsMap.get(dbName);

        if (ds == null) {
            ds = new ComboPooledDataSource(dbName);
            dsMap.put(dbName, ds);
        }
        return ds;
    }

    public static synchronized Connection getConnection()
    {
        return getConnection("default");
    }

    public static synchronized Connection getConnection(String dbName)
    {
        ComboPooledDataSource ds = getDataSource(dbName);
        Connection conn = null;
        try
        {
            conn = ds.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return conn;
    }
}

 

package com.test.common.util;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DBUtil
{
    private static Logger log = LoggerFactory.getLogger(DBUtil.class);
    private static final String DEFAULT_DB_NAME = "default";

    public static void updateSQL(String sql)
    {
        updateSQL("default", sql);
    }

    public static void updateSQL(String dbName, String sql)
    {
        QueryRunner queryRunner = new QueryRunner(DataSourceFactory.getDataSource(dbName));
        try {
            log.debug("sql:" + sql);
            int i = queryRunner.update(sql);
            SeleniumUtil.logInfo("結果:" + i + " 行數據已經更新");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static List<Map<String, Object>> getMultiResult(String sql)
    {
        return getMultiResult("default", sql);
    }

    public static List<Map<String, Object>> getMultiResult(String dbName, String sql)
    {
        List list = null;
        QueryRunner queryRunner = new QueryRunner(DataSourceFactory.getDataSource(dbName));
        try {
            log.debug("sql:" + sql);
            list = (List)queryRunner.query(sql, new MapListHandler());
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return list;
    }

    public static Map<String, Object> getSingleResult(String sql)
    {
        return getSingleResult("default", sql);
    }

    public static Map<String, Object> getSingleResult(String dbName, String sql)
    {
        Map map = null;
        QueryRunner queryRunner = new QueryRunner(DataSourceFactory.getDataSource(dbName));
        try {
            log.debug("sql:" + sql);
            map = (Map)queryRunner.query(sql, new MapHandler());
            return map;
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return map;
    }
}

 

工程的src\main\resources路徑下新增配置文件 c3p0-config.xml,程序會自動加載src下c3p0的配置文件【c3p0-config.xml】

內容如下

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!-- 命名的配置,可以通過方法調用實現 -->
    <named-config name="default">
        <property name="user">demotest</property>
        <property name="password">demotest</property>
        <property name="jdbcUrl">jdbc:oracle:thin:@192.168.200.**:****:***</property>
        <property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
        <!-- 如果池中數據連接不夠時一次增長多少個 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化數據庫連接池時連接的數量 -->
        <property name="initialPoolSize">20</property>
        <!-- 數據庫連接池中的最大的數據庫連接數 -->
        <property name="maxPoolSize">25</property>
        <!-- 數據庫連接池中的最小的數據庫連接數 -->
        <property name="minPoolSize">5</property>
    </named-config>
    <named-config name="test2">
        <property name="user">demotest2</property>
        <property name="password">demotest2</property>
        <property name="jdbcUrl">jdbc:oracle:thin:@192.168.200.**:****:***</property>
        <property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
        <!-- 如果池中數據連接不夠時一次增長多少個 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化數據庫連接池時連接的數量 -->
        <property name="initialPoolSize">20</property>
        <!-- 數據庫連接池中的最大的數據庫連接數 -->
        <property name="maxPoolSize">25</property>
        <!-- 數據庫連接池中的最小的數據庫連接數 -->
        <property name="minPoolSize">5</property>
    </named-config>

</c3p0-config>

 

DRUID連接池

Druid是阿里巴巴開發的號稱爲監控而生的數據庫連接池。可以監控數據庫訪問性能,Druid內置提供了一個功能強大的StatView插件,能夠詳細統計SQL的執行性能,這對於線上分析數據庫訪問性能有幫助。

導入jar包:

  • druid-1.1.5.jar
  • ​ mysql-connector-java-5.1.41.jar

核心類 com.alibaba.druid.pool.DruidDataSource extends DruidAbstractDataSource implements DataSource

 /*
     * 使用實現了javax.sql.DataSource接口的子類--druid連接池硬編碼
     */
    @Test
    public void test6(){
        try {
            //1.驅動註冊程序
            DruidDataSource dds=new DruidDataSource();
            dds.setDriverClassName(driverClassName);
            dds.setUrl(url);
            dds.setUsername(userName);
            dds.setPassword(userPassword);
            //2.獲取連接對象
            con=dds.getConnection();
            //3.準備sql語句
            String sql="select * from user";
            //4.創建prepareStatement
            pst=con.prepareStatement(sql);
            //5.執行sql語句,得到返回結果
            rs= pst.executeQuery();
            //6.遍歷結果,索引從1開始
            while(rs.next()){
                System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }


/*
     * 使用實現了javax.sql.DataSource接口的子類--druid連接池軟編碼
     */
    @Test
    public void test7(){
        try {
            //1.驅動註冊程序
            Properties pro=new Properties();
            DataSource dds=null;
            InputStream inStream=SqlDriverManage.class.getResourceAsStream("druid.properties");
            pro.load(inStream);
            dds=com.alibaba.druid.pool.DruidDataSourceFactory.createDataSource(pro);

            //2.獲取連接對象
            con=dds.getConnection();
            //3.準備sql語句
            String sql="select * from user";
            //4.創建prepareStatement
            pst=con.prepareStatement(sql);
            //5.執行sql語句,得到返回結果
            rs= pst.executeQuery();
            //6.遍歷結果,索引從1開始
            while(rs.next()){
                System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

 

 工程的src\main\resources路徑下新增配置文件druid.properties。druid.properties文件內容如下:

#druid.properties
driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=123456
url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=truecharacterEncoding=UTF-8&useSSL=false&serverTimezone=UTC

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章