JDBC(三)數據庫連接池(DBCP、C3P0)


前言

  這段時間狀態有一點浮躁,希望自己靜下心來。還有特別多的東西還沒有學懂。需要學習的東西非常的多,加油!

一、JDBC復習

  Java Data Base Connectivity,java數據庫連接,在需要存儲一些數據,或者拿到一些數據的時候,就需要往數據庫里存取數據。那么java如何連接數據庫呢?需要哪些步驟?

1.1、注冊驅動

  1)什么是驅動

     驅動就是JDBC實現類,通俗點講,就是能夠連接到數據庫功能的東西就是驅動,由於市面上有很多數據庫,Oracle、MySql等等,所以java就有一個連接數據庫的實現規

    范接口,定義一系列的連接數據庫接口(java.sql.Driver接口),但是不提供實現,而每個數據庫廠家來提供這些接口的具體實現,這樣一來,不管使用的是什么數據庫,我

    們開發者寫的代碼都是相同的,就不必因為數據庫的不同,而寫法不同,唯一的不同就是數據庫驅動不一樣,使用mysql,那么就必須使用mysql的驅動,使用Oracle就必

    須使用oracle的驅動實現類。

    看下面mysql連接數據的原理圖,看看驅動是在哪里,起什么作用?

    

  2)DriverManager,一個工具類,是用於操作管理JDBC實現類的

 原始寫法:DriverManager.register(new Driver());  //因為使用的是MySql,所以在導包時就需要導入com.mysql.jdbc.Driver

 現在寫法:Class.forName("com.mysql.jdbc.Driver");  //不用導包,會執行com.mysql.jdbc.Driver類中的靜態代碼塊,其靜態代碼塊的內容為
            static {
                try {                      
                 java.sql.DriverManager.registerDriver(new Driver());
                    } catch (SQLException E) {
                    throw new RuntimeException("Can't register driver!");
                             }
                        }  

  分析: 

   會發現第二種加載驅動的方法的底層其實就是第一種加載驅動。為什么要這樣呢?原因很簡單, 第一種是硬編程,直接將數據庫驅動給寫死了,無法擴展,如果使用第一

   種,那么連接的數據庫只能是mysql,因為導包導的是mysql的驅動包,如果換成Oracle,就會報錯,需要在代碼中將Oracle的驅動包導入,這樣很麻煩,而第二種寫法就不

   一樣了,第二種是使用的字符串方法注冊驅動的,我們只需要將該字符串提取到一個配置文件中,以后想換成oracle數據庫,只需要將該字符串換成oracle驅動的類全名即可

   ,而不需要到代碼中去修改什么東西。

1.2、獲取連接

  使用DriverManage來獲得連接,因為DriverManager是驅動實現類的管理者

  Connection conn = DriverManager.getConnection(url,user,password);

    url:確定數據庫服務器的位置,端口號,數據庫名

      jdbc:mysql://localhost:3306/db 

    user:登錄名稱,默認root

    password:密碼,默認root   

  這里只是說mysql,別的數據庫,url格式就不同了。

    MySQL    jdbc:mysql://localhost:3306/db    默認端口是3306,粗體為連接時使用的數據庫名

    Oracle     jdbc:oracle:thin:@localhost:1521:db  默認端口號1521

    DB2      jdbc:db2://localhost:6789/db      默認端口號6789

    SQLServer  jdbc:microsoft:sqlserver://localhost:1433;databaseName=db  默認端口號1433

    SQLServer 2005  jdbc:sqlserver://localhost:1433;databaseName=db  默認端口號1433

1.3、獲取執行sql對象,PreparedStatement對象 

  通過Connection對象獲取Statement或者PraparedStament對象(使用它)處理sql

  1)Statement

    Statement st = conn.createStatement();  //獲取sql語句執行對象

    st.excuteUpdate(sql);  //執行增刪改語句

    st.excuteQuery(sql);  //執行查詢語句      

    sql語句必須是完整的。

  2)PraparedStatment

    sql語句可以不是完整的,可以將參數用?替代,然后在預編譯后加入未知參數

    PraparedStatment ps = conn.prapareStatement(sql);  //獲取sql語句執行對象praparedStatment

    賦值

      ps.setInt(Index,value);  ps.setString(index,value);  //可以設置很多中類型,index從1開始,代表sql語句中的第幾個未知參數,

      ps.excuteUpdate();  //執行增刪改語句

      ps.excuteQuery(sql);  //執行查詢語句

 

  這兩個的區別,常使用的是PraparedStatment對象,因為它可以預編譯,效率高,可以設置參數等等優點。

1.4、獲取結果集對象

  int count = ps.excuteUpdate();   //執行增刪改的sql語句時,返回一個int類型的整數,代表數據庫表影響的行數,

  Result result = ps.excuteQuery();  //執行查詢sql語句時,返回一個結果集對象,該對象裝着所有查詢到的數據信息,一行一行的存儲數據庫表信息。

1.5、處理結果集

  對查詢到的Result結果進行處理,拿到所有數據,並封裝成對象。

        while(rs.next()){

          獲取行數據的第一種方式
          rs.getString(index);//index代表第幾列,從1開始

          獲取行數據的第二中方式
          rs.getString(string);  //string:代表字段名稱。

        }

  

  總結:java的JDBC就分為5步,4個屬性

    屬性:driver、url、user、password

    五步:

      注冊驅動、獲取連接、獲取執行sql語句對象、獲取結果集對象、處理結果。

二、JDBC的CURD操作

  創建(Create)、更新(Update)、讀取(Retrieve)和刪除(Delete)操作

  查詢所有(讀取Retrieve)

2.1、查詢所有記錄讀取(Retrieve)

@Test
    public void findAll() throws Exception{
        //1 注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        //3語句執行者,sql語句
        Statement st = conn.praparedStatement("select * from t_user");
        //4 執行查詢語句
        ResultSet rs = st.executeQuery();
        //5處理數據
        // * 如果查詢多個使用,使用while循環進行所有數據獲取
        // * 技巧:如果查詢結果最多1條,使用  if(rs.next()) {  查詢到了 } else {  沒有數據 }
        while(rs.next()){
            int id = rs.getInt(1);
            String username = rs.getString(2);
            String password =rs.getString(3);
            System.out.print(id + ", ");
            System.out.print(username + ", ");
            System.out.println(password);
        }
        //6釋放資源
        rs.close();
        st.close();
        conn.close();
        
    }
findAll()

2.2、增加操作(創建Create)  

@Test
    public void save() throws Exception{
        //1 注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        //3語句執行者
        Statement st = conn.praparedStatement("insert into t_user(username,password) values(?,?)");
                //3.1賦值
                st.setString(1,"xiaoming");
                st.setString(2,"123");
        //4 執行DML語句
        int r = st.executeUpdate();
        
        //5處理數據
        System.out.println(r);
        
        //6釋放資源
        //rs.close();
        st.close();
        conn.close();
    }
save()

2.3、更新操作 (Update)

@Test
    public void update() throws Exception{
        //1 注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        //3語句執行者
        Statement st = conn.praparedStatement("update t_user set username = ? where id = ? ");
                //3.1賦值參數
                st.setString(1,"xiaoye");
                st.setInt(2,2);
        //4 執行DML語句
        int r = st.executeUpdate();
        
        //5處理數據
        System.out.println(r);
        
        //6釋放資源
        //rs.close();
        st.close();
        conn.close();
}
update()

2.4、刪除操作(delete)

@Test
    public void delete() throws Exception{
        //1 注冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2 獲得連接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        //3語句執行者
        Statement st = conn.praparedStatement("delete from t_user where id = ?");
                //3.1賦值參數
                st.setInt(1,2);
        //4 執行DML語句
        int r = st.executeUpdate();
        
        //5處理數據
        System.out.println(r);
        
        //6釋放資源
        //rs.close();
        st.close();
        conn.close();
}
delete()

  也可以用我前面寫的JDBC連接的工具類去連接!

三、數據庫連接池

  在上面,我們在進行CRUD時,一直重復性的寫一些代碼,比如最開始的注冊驅動,獲取連接代碼,一直重復寫,通過編寫一個獲取連接的工具類后,解決了這個問題,但是又

  會出現新的問題,每進行一次操作,就會獲取一個連接,用完之后,就銷毀,就這樣一直新建連接,銷毀連接,新建,銷毀,連接Connection 創建與銷毀 比較耗時的。所以應

  該要想辦法解決這個問題?

  解決方法:

    連接池就是為了解決這個問題而出現的一個方法,為了提高性能,開發連接池,連接池中一直保持有n個連接,供調用者使用,調用者用完返還給連接池,繼續給別的調用

    者使,比如連接池中一開始就有10個連接,當有5個用戶拿走了5個連接后,池中還剩5個,當第6個用戶在去池中拿連接而前面5個連接還沒歸還時,連接池就會新建一個

    連接給第六個用戶,讓池中一直能夠保存最少5個連接,而當這樣新建了很多連接后,用戶歸還連接回來時,會比原先連接池中的10個連接更多,連接池就會設置一個池中

    最大空閑的連接數,如果超過了這個數,就會將超過的連接給釋放掉,連接池就是這樣工作的。

3.1、連接池概述

  數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重復使用一個現有的數據庫連接,而不是再重新建立一個釋放空閑時間超過最大空閑時間的數據庫連接來避

  免因為沒有釋放數據庫連接而引起的數據庫連接遺漏。這項技術能明顯提高對數據庫操作的性能。

3.2、比較應用程序直接獲取連接和使用連接池

  1)應用程序直接獲取連接

    

    缺點:用戶每次請求都需要向數據庫獲得鏈接,而數據庫創建連接通常需要消耗相對較大的資源,創建時間也較長。

          假設網站一天10萬訪問量,數據庫服務器就需要創建10萬次連接,極大的浪費數據庫的資源,並且極易造成數據庫服務器內存溢出、宕機。

  2)使用連接池連接

     

    目的:解決建立數據庫連接耗費資源和時間很多的問題,提高性能。

四、常用的數據庫連接池  

  現在很多WEB服務器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的實現,即連接池的實現。通常我們把DataSource的實現,按其英文含義稱之為數據源,

  數據源中都包含了數據庫連接池的實現。
  也有一些開源組織提供了數據源的獨立實現:
    DBCP 數據庫連接池
    C3P0 數據庫連接池
  實際應用時不需要編寫連接數據庫代碼,直接從數據源獲得數據庫的連接。程序員編程時也應盡量使用這些數據源的實現,以提升程序的數據庫訪問性能。

  DBCP、C3P0、tomcat內置連接池(JNDI)是我們開發中會用到的。

4.1、DBCP連接池

  1)概述

    DBCP 是 Apache 軟件基金組織下的開源連接池實現,使用DBCP數據源,應用程序應在系統中增加如下兩個 jar 文件:
      Commons-dbcp.jar:連接池的實現
      Commons-pool.jar:連接池實現的依賴庫
    Tomcat 的連接池正是采用該連接池來實現的。該數據庫連接池既可以與應用服務器整合使用,也可由應用程序獨立使用。

  1)獲取連接的兩種方式

    兩種方式獲得連接,使用配置文件,不使用配置文件

    1.1)不使用配置文件,自己手動設置參數

       第一:導包

      

      第二:測試例子

package com.zyh.util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.commons.dbcp.BasicDataSource;


public class TestDBCP {

    public static void main(String[] args) {
        Connection conn = TestDBCP.getConnection();
        try {
            String sql = "select id,name,price from book";
            PreparedStatement ps = conn.prepareStatement(sql);
            ResultSet rs = ps.executeQuery();
            while(rs.next()){
                String id = rs.getString(1);
                String name = rs.getString(2);
                double price = rs.getDouble(3);
                System.out.println("id="+id+",name="+name+",price="+price);
                System.out.println("-------------------------");
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
    }
    
    public static Connection getConnection(){
        //創建核心類
                BasicDataSource bds = new BasicDataSource();
                //配置4個基本參數
                bds.setDriverClassName("com.mysql.jdbc.Driver");
                bds.setUrl("jdbc:mysql:///book");
                bds.setUsername("root");
                bds.setPassword("654321");
                
                //管理連接配置
                bds.setMaxActive(50); //最大活動數
                bds.setMaxIdle(20);  //最大空閑數
                bds.setMinIdle(5);  //最小空閑數
                bds.setInitialSize(10);  //初始化個數
                
                //獲取連接
                try {
                     Connection conn = bds.getConnection();
                     return conn;
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                    }
    }

}
TestDBCP

    1.2)使用配置文件,參數寫入配置文件中即可,也就是通過配置文件來配置驅動、用戶名、密碼、等信息  

      第一:導包

      和上面的一樣的

      第二:導入配置文件dbcpconfig.properties

#連接設置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/book
username=root
password=654321

#<!-- 初始化連接 -->
initialSize=10

#最大連接數量
maxActive=50

#<!-- 最大空閑連接 -->
maxIdle=20

#<!-- 最小空閑連接 -->
minIdle=5

#<!-- 超時等待時間以毫秒為單位 6000毫秒/1000等於60秒 -->
maxWait=60000


#JDBC驅動建立連接時附帶的連接屬性屬性的格式必須為這樣:[屬性名=property;] 
#注意:"user""password" 兩個屬性會被明確地傳遞,因此這里不需要包含他們。
connectionProperties=useUnicode=true;characterEncoding=gbk

#指定由連接池所創建的連接的自動提交(auto-commit)狀態。
defaultAutoCommit=true

#driver default 指定由連接池所創建的連接的只讀(read-only)狀態。
#如果沒有設置該值,則“setReadOnly”方法將不被調用。(某些驅動並不支持只讀模式,如:Informix)
defaultReadOnly=

#driver default 指定由連接池所創建的連接的事務級別(TransactionIsolation)。
#可用值為下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
dbcpconfig.properties

      第三:獲取連接,測試例子

package com.zyh.util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class TestDBCPPro {

    public static void main(String[] args) {
        Connection conn = TestDBCPPro.getConnection();
        try {
            String sql = "select id,name,price from book";
            PreparedStatement ps = conn.prepareStatement(sql);
            ResultSet rs = ps.executeQuery();
            while(rs.next()){
                String id = rs.getString(1);
                String name = rs.getString(2);
                double price = rs.getDouble(3);
                System.out.println("id="+id+",name="+name+",price="+price);
                System.out.println("-------------------------");
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    public static Connection getConnection(){
         //通過類加載器獲取指定配置文件的輸入流,TestDBCPPro是一個類名
        InputStream in = TestDBCPPro.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
        Properties properties = new Properties();
        try {
                properties.load(in);
                //加載配置文件,獲得配置信息
                DataSource ds = BasicDataSourceFactory.createDataSource(properties);
                Connection conn = ds.getConnection();
                return conn;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    

}
TestDBCPPro

    1.3)編寫一個數據源工具類

package com.zyh.util;


import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import javax.imageio.stream.FileImageInputStream;
import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DBCPUtils {
    private static DataSource ds = null;
    static{
        Properties prop = new Properties();
        try {
            prop.load(DBCPUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"));//根據DBCPUtil的classes的路徑,加載配置文件
            ds = BasicDataSourceFactory.createDataSource(prop);//得到一個數據源 
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化錯誤,請檢查配置文件");
        }
    }
    
    public static Connection getConnection(){
        try {
            return ds.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException("服務器忙。。。");
        }
    }
    
    public static void release(Connection conn,Statement stmt,ResultSet rs){
        //關閉資源
                if(rs!=null){
                    try {
                        rs.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    rs = null;
                }
                if(stmt!=null){
                    try {
                        stmt.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    stmt = null;
                }
                if(conn!=null){
                    try {
                        conn.close();//關閉
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    conn = null;
                }
    }
    
}
DBCPUtils

4.2、C3P0

  1)導包

  

  2)從配置信息中獲取  配置文件必須為xml(c3p0-config.xml)

<c3p0-config>
    <!-- 默認配置,如果沒有指定則使用這個配置 -->
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/book</property>
        <property name="user">root</property>
        <property name="password">654321</property>
    
        <property name="checkoutTimeout">30000</property>
        <property name="idleConnectionTestPeriod">30</property>
        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
        <user-overrides user="test-user">
            <property name="maxPoolSize">10</property>
            <property name="minPoolSize">1</property>
            <property name="maxStatements">0</property>
        </user-overrides>
    </default-config> 
    <!-- 命名的配置 -->
    <named-config name="jxpx">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/myums</property>
        <property name="user">root</property>
        <property name="password">root</property>
    <!-- 如果池中數據連接不夠時一次增長多少個 -->
        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">20</property>
        <property name="minPoolSize">10</property>
        <property name="maxPoolSize">40</property>
        <property name="maxStatements">0</property>
        <property name="maxStatementsPerConnection">5</property>
    </named-config>
</c3p0-config>
c3p0-config.xml  

    從配置文件中看,需要注意一個地方,一個是default-config,一個是name-config,兩者都區別在於創建核心類對象時,如果將name-config作為參數傳進去,

    那么將會調用name-config下的配置信息,否則將調用default-config下的配置信息,

    兩種方式使用c3p0,加參數,使用named-config 的配置信息不加參數,自動加載配置信息,加載的是default-config中的信息。

        //1 c3p0...jar 將自動加載配置文件。規定:WEB-INF/classes (src)  c3p0-config.xml,也就是將配置文件放在src下就會自動加載。
         //ComboPooledDataSource dataSource = new ComboPooledDataSource(); //自動從配置文件 <default-config>
         ComboPooledDataSource dataSource = new ComboPooledDataSource(); //手動指定配置文件 <named-config name="jxpx">
         Connection conn = dataSource.getConnection();
         System.out.println(conn);

 

  3)編寫一個數據源工具類

  這個工具類的xml是:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
  <default-config>
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/book/property>
    <property name="user">root</property>
    <property name="password">654321</property>
    <property name="initialPoolSize">10</property>
    <property name="maxIdleTime">30</property>
    <property name="maxPoolSize">100</property>
    <property name="minPoolSize">10</property>

  </default-config>

</c3p0-config>
c3p0-config.xml

  工具類

package com.zyh.util;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {
    //得到一個數據源
    private static DataSource dataSource = new ComboPooledDataSource();
    
    //從數據源中得到一個連接對象
    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException("服務器錯誤");
        }
    }
    
    public static void release(Connection conn,Statement stmt,ResultSet rs){
        //關閉資源
                if(rs!=null){
                    try {
                        rs.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    rs = null;
                }
                if(stmt!=null){
                    try {
                        stmt.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    stmt = null;
                }
                if(conn!=null){
                    try {
                        conn.close();//關閉
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    conn = null;
                }
    }
    
}
C3P0Util

4.3、用JavaWeb服務器管理數據源:Tomcat

  開發JavaWeb應用,必須使用一個JavaWeb服務器,JavaWeb服務器都內置數據源
  Tomcat:(DBCP)
  數據源只需要配置服務器即可
  配置數據源的步驟:
    1)拷貝數據庫連接的jar到tomcatlib目錄下
    2)配置數據源XML文件
      a)如果把配置信息寫在tomcat下的conf目錄的context.xml中,那么所有應用都能使用此數據源。
      b)如果是在當前應用的META-INF中創建context.xml, 編寫數據源,那么只有當前應用可以使用。

<Context>
  <Resource name="jdbc/datasource" auth="Container"
            type="javax.sql.DataSource" username="root" password="654321"
            driverClassName="com.mysql.jdbc.Driver" 
          url="jdbc:mysql://localhost:3306/book"
            maxActive="8" maxIdle="4"/>
</Context>
context.xml

  在servlet中進行測試:

  Context initCtx = new InitialContext();
  Context envCtx = (Context) initCtx.lookup("java:comp/env");  //path
  dataSource = (DataSource)envCtx.lookup("jdbc/datasource");   //context.xml中resource的name

  注意:此種配置下,驅動jar文件需放置在tomcat的lib下  

  擴展:

    JNDI技術介紹:

      1)JNDI(Java Naming and Directory Interface),Java命名和目錄接口,它對應於J2SE中的javax.naming包。
      2)這套API的主要作用在於:

        它可以把Java對象放在一個容器中(JNDI容器),並為容器中的java對象取一個名稱,以后程序想獲得Java對象,只需通過名稱檢索即可
      3)其核心API為Context,它代表JNDI容器,其lookup方法為檢索容器中對應名稱的對象。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM