數據庫 (二)--JDBC

JDBC概述

JDBC(JavaDataBase Connectivity)就是Java數據庫鏈接,說白了就是用Java語言來操做數據庫。原來咱們操做數據庫是在控制檯使用SQL語句來操做數據庫,JDBC是用Java語言向數據庫發送SQL語句。java

JDBC原理

早期SUN公司的天才們想編寫一套能夠鏈接天下全部數據庫的API,可是當他們剛剛開始時就發現這是不可完成的任務,由於各個廠商的數據庫服務器差別太大了。後來SUN開始與數據庫廠商們討論,最終得出的結論是,由SUN提供一套訪問數據庫的規範(就是一組接口),並提供鏈接數據庫的協議標準,而後各個數據庫廠商會遵循SUN的規範提供一套訪問本身公司的數據庫服務器的API出現。SUN提供的規範命名爲JDBC,而各個廠商提供的,遵循了JDBC規範的,能夠訪問本身數據庫的API被稱之爲驅動!
image
JDBC是接口,而JDBC驅動纔是接口的實現,沒有驅動沒法完成數據庫鏈接!每一個數據庫廠商都有本身的驅動,用來鏈接本身公司的數據庫。
固然還有第三方公司專門爲某一數據庫提供驅動,這樣的驅動每每不是開源免費的!mysql

JDBC鏈接數據庫(六個步驟)

  • 裝載數據庫驅動程序;
  • 創建數據庫鏈接;
  • 建立數據庫操做對象;
  • 訪問數據庫,執行sql語句;
  • 處理返回結果集;
  • 斷開數據庫鏈接。

鏈接角度看 JDBC

image

  1. TCP創建鏈接的三次握手;
  2. MySQL認證的三次握手;
  3. 真正的SQL執行;
  4. MySQL的關閉;
  5. TCP的四次握手關閉;

嘗試鏈接

import org.junit.Test;
import java.sql.*;

public class TestJDBC {
    @Test  //基本查詢語句d
    public void statement() throws Exception {
        //驅動
        String driver ="com.mysql.jdbc.Driver";
        Class.forName(driver);

        //數據庫連接
        String url ="jdbc:mysql://localhost:3306/jtdb";
        Connection cn = DriverManager.getConnection(url,"root","1234");

        //獲得語句對象,它來執行sql語句
        Statement stat = cn.createStatement(); //建立語句對象

        //查詢返回結果集 resultset
        String sql = "select * from dept";
        ResultSet rs = stat.executeQuery(sql);  //執行查詢的語句

        //打印數據庫表元數據,列名,dept字段數
        int cols = rs.getMetaData().getColumnCount();
        for(int i = 1 ; i <= cols ; i++ ){
            System.out.print(rs.getMetaData().getColumnName(i)+"\t");
        }
        System.out.println();//換行

        //打印表中數據
        while(rs.next()){
            for(int i =1 ; i<=cols ;i++){
                //rs.getString(i);  //獲取第幾列值,返回字符串類型
                System.out.print(rs.getString(i)+"\t");
            }
            System.out.println();  //每一條數據打印完畢換行
        }
    }

    @Test //預編譯查詢語句
    public void prepareStatment() throws Exception {
        String driver ="com.mysql.jdbc.Driver"; //驅動
        String url = "jdbc:mysql://localhost:3306/jtdb"; //連接

        //預編譯sql中用?做爲佔位符,未來會被真實值替換掉
        String sql = "select * from dept where deptno = ?" ;

        Class.forName(driver);
        Connection cn = DriverManager.getConnection(url,"root","1234");  //獲取連接

        //帶參數
        PreparedStatement ps = cn.prepareStatement(sql);  //返回預編譯對象
        ps.setString(1,"3");  //參數1:指?的位置 可能有多個;參數2:是參數值

        //查詢
        ResultSet rs =ps.executeQuery();
        int cols = rs.getMetaData().getColumnCount(); //列的總數
        for (int i = 1 ; i <= cols ; i++){
            //打印列的名稱
            System.out.print(rs.getMetaData().getColumnName(i)+"\t");
        }

        //打印數據
        while(rs.next()){//每次向下取一條,直到結尾
            System.out.println();//換行
            for(int i = 1 ; i <= cols ; i++){
                System.out.print(rs.getString(i)+"\t");
            }
        }
    }

}

參數含義

DriverManager

驅動程序管理器是負責管理驅動程序的,驅動註冊之後,會保存在DriverManager中的已註冊列表中後續的處理就能夠對這個列表進行操做。sql

Class.forName("com.mysql.jdbc.Driver");//加載驅動
Connection

在JDBC中鏈接被抽象爲Connection的鏈接對象數據庫

DriverManager.getConnection(url,username,password)
Statement

用於執行不帶參數的簡單 SQL 語句,每次執行 SQL語句,數據庫都要執行 SQL 語句的編譯c#

Statement stat = connection.createStatement();

prepareStatement與Statement的區別
一、Statement用於執行靜態SQL語句,在執行時,必須指定一個事先準備好的SQL語句。
二、PrepareStatement是預編譯的SQL語句對象,sql語句被預編譯並保存在對象中。被封裝的sql語句表明某一類操做,語句中能夠包含動態參數「?」,在執行時能夠爲「?」動態設置參數值。
三、使用PrepareStatement對象執行sql時,sql被數據庫進行解析和編譯,而後被放到命令緩衝區,每當執行同一個PrepareStatement對象時,它就會被解析一次,但不會被再次編譯。在緩衝區能夠發現預編譯的命令,而且能夠重用。
四、PrepareStatement能夠減小編譯次數提升數據庫性能。服務器

ResultSet

當咱們查詢數據庫時,返回的是一個二維的結果集,咱們這時候須要使用 ResultSet 來遍歷結果集,獲取每一行的數據。多線程

ResultSet rs = stat.executeQuery(sql);

數據庫鏈接池

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

池化

鏈接池相似於線程池或者對象池,數據庫鏈接池爲系統的運行帶來如下優點:性能

  • 昂貴的數據庫鏈接資源獲得重用;
  • 減小數據庫鏈接創建和釋放的時間開銷,提升系統響應速度;
  • 統一的數據庫鏈接管理,避免鏈接資源的泄露。

數據庫鏈接池的工做原理

  • 鏈接池的創建
  • 鏈接池中鏈接的使用管理
  • 鏈接池的關閉

第1、鏈接池的創建。
通常在系統初始化時,鏈接池會根據系統配置創建,並在池中建立了幾個鏈接對象,以便使用時能從鏈接池中獲取。鏈接池中的鏈接不能隨意建立和關閉,這樣避免了鏈接隨意創建和關閉形成的系統開銷。
Java中提供了不少容器類能夠方便的構建鏈接池,例如Vector、Stack等。url

第2、鏈接池的管理。
鏈接池管理策略是鏈接池機制的核心,鏈接池內鏈接的分配和釋放對系統的性能有很大的影響。其管理策略是:
當客戶請求數據庫鏈接時,首先查看鏈接池中是否有空閒鏈接,若是存在空閒鏈接,則將鏈接分配給客戶使用;若是沒有空閒鏈接,則查看當前所開的鏈接數是否已經達到最大鏈接數,若是沒達到就從新建立一個鏈接給請求的客戶;若是達到就按設定的最大等待時間進行等待,若是超出最大等待時間,則拋出異常給客戶。
當客戶釋放數據庫鏈接時,先判斷該鏈接的引用次數是否超過了規定值,若是超過就從鏈接池中刪除該鏈接,不然保留爲其餘客戶服務。
該策略保證了數據庫鏈接的有效複用,避免頻繁的創建、釋放鏈接所帶來的系統資源開銷。

第3、鏈接池的關閉。
當應用程序退出時,關閉鏈接池中全部的鏈接,釋放鏈接池相關的資源,該過程正好與建立相反。

鏈接池須要注意的點

一、併發問題
爲了使鏈接管理服務具備最大的通用性,必須考慮多線程環境,即併發問題。
這個問題相對比較好解決,由於各個語言自身提供了對併發管理的支持像java,c#等等,使用synchronized(java)lock(C#)關鍵字便可確保線程是同步的。
二、事務處理
咱們知道,事務具備原子性,此時要求對數據庫的操做符合「ALL-OR-NOTHING」原則,即對於一組SQL語句要麼全作,要麼全不作。
咱們知道當2個線程共用一個鏈接Connection對象,並且各自都有本身的事務要處理時候,對於鏈接池是一個很頭疼的問題,由於即便Connection類提供了相應的事務支持,但是咱們仍然不能肯定那個數據庫操做是對應那個事務的,這是因爲咱們有2個線程都在進行事務操做而引發的。
爲此咱們可使用每個事務獨佔一個鏈接來實現,雖然這種方法有點浪費鏈接池資源可是能夠大大下降事務管理的複雜性。
三、鏈接池的分配與釋放
鏈接池的分配與釋放,對系統的性能有很大的影響。合理的分配與釋放,能夠提升鏈接的複用度,從而下降創建新鏈接的開銷,同時還能夠加快用戶的訪問速度。
對於鏈接的管理可以使用一個List。即把已經建立的鏈接都放入List中去統一管理。每當用戶請求一個鏈接時,系統檢查這個List中有沒有能夠分配的鏈接。若是有就把那個最合適的鏈接分配給他,若是沒有就拋出一個異常給用戶。
4、鏈接池的配置與維護鏈接池中到底應該放置多少鏈接,才能使系統的性能最佳?系統可採起設置最小鏈接數(minConnection)和最大鏈接數(maxConnection)等參數來控制鏈接池中的鏈接。比方說,最小鏈接數是系統啓動時鏈接池所建立的鏈接數。若是建立過多,則系統啓動就慢,但建立後系統的響應速度會很快;若是建立過少,則系統啓動的很快,響應起來卻慢。這樣,能夠在開發時,設置較小的最小鏈接數,開發起來會快,而在系統實際使用時設置較大的,由於這樣對訪問客戶來講速度會快些。最大鏈接數是鏈接池中容許鏈接的最大數目,具體設置多少,要看系統的訪問量,可經過軟件需求上獲得。如何確保鏈接池中的最小鏈接數呢?有動態和靜態兩種策略。動態即每隔必定時間就對鏈接池進行檢測,若是發現鏈接數量小於最小鏈接數,則補充相應數量的新鏈接,以保證鏈接池的正常運轉。靜態是發現空閒鏈接不夠時再去檢查。

相關文章
相關標籤/搜索