JDBC完全解析

前言

以前做項目的時候用到了JDBC操作mysql數據庫,後面百度面試問到數據庫的時候又忘了,最近幾天把項目重新看了一遍,查了相關的文章把JDBC做一個總結。

JDBC的結構

JDBC(Java DataBase Connectivity,java數據庫連接)是一種用於執行SQL語句的Java API,可以爲多種關係數據庫提供統一訪問,它由一組用Java語言編寫的類和接口組成。JDBC提供了一種基準,據此可以構建更高級的工具和接口,使數據庫開發人員能夠編寫數據庫應用程序。Java中使用JDBC的整體結構如下圖:
這裏寫圖片描述

JDBC的用途

簡單地說,JDBC 主要做四件事:①、加載對應數據庫驅動 (Load Driver) ②、與數據庫建立連接(connection) ③、發送 操作數據庫的語句(createStatement) ④、執行並處理返回結果(executeQuery)如下圖:

這裏寫圖片描述

下面是四個步驟簡單的代碼:

/*1 加載驅動JDBC_DRIVER="com.mysql.jdbc.Driver"*/

Class.forName(JDBC_DRIVER); 

/*2 建立連接DB_URL數據庫的url,  USER 數據庫的用戶名, PASS 數據庫密碼*/

Connection conn = (Connection) DriverManager.getConnection(DB_URL, USER, PASS);

/*3  sql = "select * from student"預編譯sql語句*/
PreparedStatement pstmt=conn.prepareStatement(sql);

/*4執行sql語句並返回結果*/
ResultSet rs = pstmt.executeQuery();

安裝mysql以及Navicat for mysql

上面以及簡單的講了一下jdbc爲了實現在eclipse上運行操作數據庫的效果,必須得先安裝某種數據庫,我安裝的是mysql數據庫爲了方便操作最好安裝mysql數據庫可視化工具Navicat for mysql。安裝好了之後就可以使用Navicat for mysql來管理數據庫了。關於如何安裝mysql和使Navicat for mysql用可以看下這篇文章MySQL安裝安裝好了之後我是用Navicat for mysql建立了一個students的數據庫以方便後面的測試。數據庫如下圖
這裏寫圖片描述
student表很簡單就一個id 一個name 一個age 然後自己添加了四條數據。

eclipse配置及運行

首先在eclipse中新建一個DateBase的項目,往項目中添加依賴包mysql-connector-java-5.0.8-bin.jar,然後就來看看如何對上面的數據庫進行簡單的:插入(insert),更新(update),查詢(select)等操作


import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;


public class JDBCDemo {
    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  //MySQL數據庫驅動
    static final String DB_URL = "jdbc:mysql://localhost/students";//數據庫地址
    static final String USER = "hanking";//用戶名
    static final String PASS = "hu123";//密碼
    public static void main(String[] args) {
        try {
            insert();
            update();
            select() ;
        } catch (Exception e) {

            e.printStackTrace();
        }
    }

//  insert() 往數據庫中插入一條數據
    private static void insert() throws Exception {

        Connection conn = null;
        PreparedStatement pstsm = null;
        try {
            // 1:調用工具類獲取連接
            conn = getConnection();

            // 2:準備sql預編譯語句
            // ?佔用一個參數位
            String sql = "INSERT INTO student (id,name,age) VALUES (?,?,?);";

            // 3:執行sql預編譯語句(檢查語法)
            pstsm =(PreparedStatement) conn.prepareStatement(sql);

            // 4:設置傳遞的參數

            pstsm .setInt(1, 5);
            pstsm .setString(2, "hanking");
            pstsm .setInt(3, 20);

            // 5:發送參數,執行sql
            // 注意:這裏的方法後面沒有參數
            int result =  pstsm .executeUpdate();
            System.out.println("影響了" + result + "行");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
             pstsm.close();
             conn.close();
            // 6:關閉連接
        }
    }

//  更新數據庫中的一條數據 
     private static int update() {
            Connection conn = getConnection();
            int i = 0;
            String sql = "UPDATE student SET age = ? WHERE id = ?;";
            PreparedStatement pstmt;
            try {
                pstmt = (PreparedStatement) conn.prepareStatement(sql);
                pstmt.setInt(1, 20);
                pstmt.setInt(2, 1);            
                i = pstmt.executeUpdate();
                System.out.println("resutl: " + i);
                pstmt.close();
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return i;
        }

//選擇出數據庫中所有的數據      
     private static void select() {   
            Connection conn = getConnection();
            String sql = "select * from student";
            PreparedStatement pstmt;
            try {
                pstmt = (PreparedStatement) conn.prepareStatement(sql);
                ResultSet rs = pstmt.executeQuery();
                int col = rs.getMetaData().getColumnCount();
                System.out.println("============================");
                while (rs.next()) {
                    for (int i = 1; i <= col; i++) {
                        System.out.print(rs.getString(i) + "\t");
                        if ((i == 2) && (rs.getString(i).length() < 8)) {
                            System.out.print("\t");
                        }
                     }
                    System.out.println("");
                }
                    System.out.println("============================");
            } catch (SQLException e) {
                e.printStackTrace();
            }

        }
    private static Connection getConnection() {

        Connection conn = null;
        try {
            Class.forName(JDBC_DRIVER); //classLoader,加載對應驅動
            conn = (Connection) DriverManager.getConnection(DB_URL, USER, PASS);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
}
//最後輸出如下
============================
1   hust0       20  
2   hust1       24  
3   hust2       25  
4   hust3       23  
5   hanking     20  
============================

數據庫連接池

上面的例子簡單的實現了通過JDBC對數據庫進行的簡單操作,然而實際項目中往往會使用到數據庫連接池,原因:對於一個簡單的數據庫應用,由於對於數據庫的訪問不是很頻繁。這時可以簡單地在需要訪問數據庫時,就新創建一個連接,用完後就關閉它,這樣做也不會帶來什麼明顯的性能上的開銷。但是對於一個複雜的數據庫應用,情況就完全不同了。頻繁的建立、關閉連接,會極大的減低系統的性能,因爲對於連接的使用成了系統性能的瓶頸。
這裏寫圖片描述

數據庫連接池的功能

數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重複使用一個現有的數據庫連接,而不是再重新建立一個;釋放空閒時間超過最大空閒時間的數據庫連接來避免因爲沒有釋放數據庫連接而引起的數據庫連接遺漏。連接複用,通過建立一個數據庫連接池以及一套連接使用管理策略,使得一個數據庫連接可以得到高效、安全的複用,避免了數據庫連接頻繁建立、關閉的開銷。

數據庫連接池的優點

1. 資源重用

由於數據庫連接得到重用,避免了頻繁創建、釋放連接引起的大量性能開銷。在減少系統消耗的基礎上,另一方面也增進了系統運行環境的平穩性(減少內存碎片以及數據庫臨時進程/線程的數量)。

2. 更快的系統響應速度

數據庫連接池在初始化過程中,往往已經創建了若干數據庫連接置於池中備用。此時連接的初始化工作均已完成。對於業務請求處理而言,直接利用現有可用連接,避免了數據庫連接初始化和釋放過程的時間開銷,從而縮減了系統整體響應時間。

3. 統一的連接管理,避免數據庫連接泄漏

在較爲完備的數據庫連接池實現中,可根據預先的連接佔用超時設定,強制收回被佔用連接。從而避免了常規數據庫連接操作中可能出現的資源泄漏。

數據庫連接池的實現

在eclipse中新建一個項目,在項目中加入commons-dbcp-1.4.jar,commons-pool-1.5.6.jar這兩個依賴包,還是使用上面的那個數據庫進行測試。代碼如下



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

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;


public class BasicDataSourceExample {

    public static void main(String[] args) {

        System.out.println("Setting up data source.");

        //創建數據庫連接池

        DataSource dataSource = setupDataSource("jdbc:mysql://127.0.0.1:3306/students");
        System.out.println("Done.");

        //
        // Now, we can use JDBC DataSource as we normally would.
        //
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rset = null;

        try {
            System.out.println("Creating connection.");

            conn = dataSource.getConnection();

            System.out.println("Creating statement.");

            String sql="SELECT *from student";

            stmt = conn.prepareStatement(sql);

            System.out.println("Executing statement.");

            rset = stmt.executeQuery();

         //輸出正在使用的數據庫連接和空閒的數據庫連接   
            printDataSourceStats(dataSource);

            System.out.println("Results:");

            int numcols = rset.getMetaData().getColumnCount();

            while(rset.next()) {
                for(int i=1;i<=numcols;i++) {
                    System.out.print("\t" + rset.getString(i));
                }
                System.out.println("");
            }
        } catch(SQLException e) {
            e.printStackTrace();
        } finally {
            try { if (rset != null) rset.close(); } catch(Exception e) { }
            try { if (stmt != null) stmt.close(); } catch(Exception e) { }
            try { if (conn != null) conn.close(); } catch(Exception e) { }
            try {
                //關閉數據庫連接池
                shutdownDataSource(dataSource);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static DataSource setupDataSource(String connectURI) {
        BasicDataSource ds = new BasicDataSource();
        ds.setUsername("hanking");  
        ds.setPassword("hu123");  
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl(connectURI);
        //設置初始化連接數爲5
        ds.setInitialSize(5);
        //設置最大連接數爲20
        ds.setMaxActive(20);
        //設置最大空閒數爲 10
        ds.setMaxIdle(10);
        return ds;
    }

    public static void printDataSourceStats(DataSource ds) {
        BasicDataSource bds = (BasicDataSource) ds;
        System.out.println("NumActive: " + bds.getNumActive());
        System.out.println("NumIdle: " + bds.getNumIdle());
    }

    public static void shutdownDataSource(DataSource ds) throws SQLException {
        BasicDataSource bds = (BasicDataSource) ds;
        bds.close();
        System.out.println("bds closed");
    }
}


//輸出結果
/*Setting up data source.
Done.
Creating connection.
Creating statement.
Executing statement.
NumActive: 1
NumIdle: 4
Results:
    1   hust0   20
    2   hust1   24
    3   hust2   25
    4   hust3   23
    5   hanking 20
bds closed
*/

由上面代碼可知使用數據庫連接池很簡單,首先建立一個數據庫連接池dataSource ,然後從數據庫連接池中獲得一個空閒的數據庫連接conn = dataSource.getConnection() 後面的步驟就和JDBC中的操作是一樣的。上面是最簡單的例子,還可以對數據庫連接池的各種參數進行設置。比如說數據庫連接池最大連接數,最小連接數等等。

發佈了186 篇原創文章 · 獲贊 675 · 訪問量 64萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章