JDBC的擴展知識點總結

一、數據庫的事務

1.1 事務概述

  • 事務:一組邏輯操作單元,使數據從一種狀態變換到另一種狀態
  • 事務處理(事務操作):保證所有事務都作為一個工作單元來執行,即使出現瞭故障,都不能改變這種執行方式。當在一個事務中執行多個操作時,要麼所有的事務都被提交(commit),那麼這些修改就永久地保存下來;要麼數據庫管理系統將放棄所作的所有修改,整個事務回滾(rollback)到最初狀態
  • 為確保數據庫中數據的一致性,數據的操縱應當是離散的成組的邏輯單元:當它全部完成時,數據的一致性可以保持,而當這個單元中的一部分操作失敗,整個事務應全部視為錯誤,所有從起始點以後的操作應全部回退到開始狀態
  • 舉個栗子,當小明給小紅轉賬時,因為種種原因沒有轉賬成功,小明的錢減少瞭,小紅卻沒有收到錢,此時就需要事務回滾,否則小明就得哭死…

1.2 事務的屬性

事務的ACID(acid)屬性

  • 原子性(Atomicity)
  • 原子性是指事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生。
  • 一致性(Consistency)
  • 事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態。
  • 隔離性(Isolation)
  • 事務的隔離性是指一個事務的執行不能被其他事務幹擾,即一個事務內部的操作及使用的數據對並發的其他事務是隔離的,並發執行的各個事務之間不能互相幹擾。
  • 持久性(Durability)
  • 持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來的其他操作和數據庫故障不應該對其有任何影響

1.3 JDBC事務處理

當一個連接對象被創建時,默認情況下是自動提交事務:每次執行一個 SQL 語句時,如果執行成功,就會向數據庫自動提交,而不能回滾

為瞭讓多個 SQL 語句作為一個事務執行:

1. 調用 Connection 對象的 setAutoCommit(false); 以取消自動提交事務

2.在所有的 SQL 語句都成功執行後,調用 commit(); 方法提交事務

3.在出現異常時,調用 rollback(); 方法回滾事務

4.若此時 Connection 沒有被關閉, 則需要恢復其自動提交狀態

1.4 數據庫事務使用的過程

使用數據庫的事務,我們需要配合異常處理try

public void testJDBCTransaction() {
Connection conn = null;
try {
// 1.獲取數據庫連接
conn = JDBCUtils.getConnection();
// 2.開啟事務
conn.setAutoCommit(false);
// 3.進行數據庫操作

// 4.若沒有異常,則提交事務
conn.commit();
} catch (Exception e) {
e.printStackTrace();
// 5.若有異常,則回滾事務
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}} finally {
JDBCUtils.close(null, null, conn); }  }

1.5 使用數據庫事務的好處

使用COMMIT 和 ROLLBACK語句,我們可以:

  • 確保數據完整性。
  • 數據改變被提交之前預覽。
  • 將邏輯上相關的操作分組

提交或回滾前的數據狀態

  • 改變前的數據狀態是可以恢復的
  • 執行 DML 操作的用戶可以通過 SELECT 語句查詢提交或回滾之前的修正
  • 其他用戶不能看到當前用戶所做的改變,直到當前用戶結束事務。
  • DML語句所涉及到的行被鎖定, 其他用戶不能操作

提交後的數據狀態

  • 數據的改變已經被保存到數據庫中。
  • 改變前的數據已經丟失。
  • 所有用戶可以看到結果。
  • 鎖被釋放, 其他用戶可以操作涉及到的數據

說瞭這麼多,還是使用代碼來舉例說明更加清晰(數據庫連接的JDBCUtils類上一章寫過瞭):

package com.company.jdbcDemo;

import com.company.jdbcDemo.JDBCUtils;

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

/*
    AA給CC轉賬1000
    AA 2000
    CC 2000

        try{
           開啟事務
           AA -= 1000
           System.out.println(1/0);
           CC += 1000
           提交(一旦提交數據不能再回滾(撤銷))
        }catch(Exception e){
        事務回滾(撤銷)
        }

    CREATE TABLE account(
    NAME VARCHAR(20),
    balance INT
    )

 */
public class AccountDemo {

    public static void main(String[] args) throws SQLException {
        //1.獲取數據庫連接對象
        Connection connection = JDBCUtils.getConnection();
        PreparedStatement ps = null;
        try {
            //2.開啟事物--禁止自動提交
            connection.setAutoCommit(false);
            //-------------------------------------------------------------------
            //3.做具體的操作---執行sql語句
            //預編譯
            String sql = "update account set balance=? where name=?";
            ps = connection.prepareStatement(sql);

            //給占位符賦值
            ps.setInt(1, 1000);
            ps.setString(2, "aa");
            //執行sql
            ps.executeUpdate();

            System.out.println(1 / 0);

            //給占位符賦值
            ps.setInt(1, 3000);
            ps.setString(2, "cc");
            //執行sql
            ps.executeUpdate();
            //-------------------------------------------------------------------
            //4.事務提交
            connection.commit();
        }catch (Exception e){
            e.printStackTrace();
            //5.事務回滾
            connection.rollback();
        }finally {
            //6.允許自動提交
            connection.setAutoCommit(true);
            //7.關閉資源----最後關閉資源
            JDBCUtils.close(ps,connection);

        }
    }
}

二、數據庫連接池

概述

前面我們的示例代碼中,一直在調用我寫的那個JDBCUtils類來完成數據庫的連接,如果我們在別的工程中,就需要復制一下我的那個類,並且我寫的那個也不是很嚴謹,我前面實現的數據庫連接方式存在以下問題:

  •  普通的JDBC數據庫連接使用 DriverManager 來獲取,每次向數據庫建立連接的時候都要將 Connection 加載到內存中,再驗證用戶名和密碼
  • 數據庫的連接資源並沒有得到很好的重復利用.若同時有幾百人甚至幾千人在線,頻繁的進行數據庫連接操作將占用很多的系統資源,嚴重的甚至會造成服務器的崩潰
  • 對於每一次數據庫連接,使用完後都得斷開。否則,如果程序出現異常而未能關閉,將會導致數據庫系統中的內存泄漏,最終將導致重啟數據庫
  • 這種開發不能控制被創建的連接對象數,系統資源會被毫無顧及的分配出去,如連接過多,也可能導致內存泄漏,服務器崩潰

使用數據庫連接池

  • 數據庫連接池的基本思想就是為數據庫連接建立一個“緩沖池”。預先在緩沖池中放入一定數量的連接,當需要建立數據庫連接時,隻需從“緩沖池”中取出一個,使用完畢之後再放回去
  • 數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重復使用一個現有的數據庫連接,而不是重新建立一個
  • 數據庫連接池在初始化時將創建一定數量的數據庫連接放到連接池中,這些數據庫連接的數量是由最小數據庫連接數來設定的。無論這些數據庫連接是否被使用,連接池都將一直保證至少擁有這麼多的連接數量。連接池的最大數據庫連接數量限定瞭這個連接池能占有的最大連接數,當應用程序向連接池請求的連接數超過最大連接數量時,這些請求將被加入到等待隊列中

2.1 數據庫連接池技術的優點

  • 資源重用
  • 由於數據庫連接得以重用,避免瞭頻繁創建,釋放連接引起的大量性能開銷。在減少系統消耗的基礎上,另一方面也增加瞭系統運行環境的平穩性。
  • 更快的系統反應速度
  • 數據庫連接池在初始化過程中,往往已經創建瞭若幹數據庫連接置於連接池中備用。此時連接的初始化工作均已完成。對於業務請求處理而言,直接利用現有可用連接,避免瞭數據庫連接初始化和釋放過程的時間開銷,從而減少瞭系統的響應時間
  • 新的資源分配手段
  • 對於多應用共享同一數據庫的系統而言,可在應用層通過數據庫連接池的配置,實現某一應用最大可用數據庫連接數的限制,避免某一應用獨占所有的數據庫資源
  • 統一的連接管理,避免數據庫連接泄露
  • 在較為完善的數據庫連接池實現中,可根據預先的占用超時設定,強制回收被占用連接,從而避免瞭常規數據庫連接操作中可能出現的資源泄露

三、DRUID(德魯伊)

  • 瞭解瞭數據庫連接池以及優勢,接下來我為大傢帶來一個目前常用的一個數據庫連接池框架—>德魯伊
  • DRUID是阿裡巴巴開源平臺上一個數據庫連接池實現,它結合瞭C3P0、DBCP、PROXOOL等DB池的優點,同時加入瞭日志監控,可以很好的監控DB池連接和SQL的執行情況,可以說是針對監控而生的DB連接池,據說是目前最好的連接池

3.1 德魯伊的使用

阿裡德魯伊連接池技術首先分為兩步:

1.加入jar包

例如:druid-1.1.10.jar

2.代碼步驟

第一步:建立一個數據庫連接池

第二步:設置連接池的參數

第三步:獲取連接

使用德魯伊連接數據庫的方式一

//1、創建數據源(數據庫連接池)對象
DruidDataSource ds =new DruidDataSource();
//2、設置參數
//(1)設置基本參數
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test");
ds.setUsername("root");
ds.setPassword("mysql123");
//3、獲取連接
Connection conn = ds.getConnection();
//如果這裡沒有關閉,就相當於沒有還
conn.close();

使用德魯伊連接數據庫的方式二

// 創建配置文件druid.properties
url=jdbc:mysql://localhost:3306/0319db ?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver

代碼如下:
Properties pro = new Properties();
pro.load(TestDruid2.class.getClassLoader().getResourceAsStream("druid.properties"));
DataSource ds=
		DruidDataSourceFactory.createDataSource(pro);
Connection conn = ds.getConnection();

這裡註意瞭,德魯伊配置文件中的key,必須跟我下面一樣,否則連接不成功哦

// druid.properties內容
url=jdbc:mysql://localhost:3306/demo
username=root
password=123321
driverClassName=com.mysql.jdbc.Driver

四、DBUtils工具類

  • 既然數據庫連接有瞭數據庫連接池這麼方便地操作,那麼對數據的增刪改查有沒有相關的方法呢?當然有,它就是DBUtils
  • 將常用的操作數據庫的JDBC的類和方法集合在一起,就是DBUtils.
  • 這個比較簡單,我就介紹一下常用的API操作,具體的知識點,可以查看Java的API文檔

在這裡插入圖片描述

我就直接上操作瞭,這裡還是使用我前面實現的那個JDBCUtils類哈,偷個懶…

package com.company.jdbc2;

import com.company.jdbc.JDBCUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;

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

/*
    通過使用DBUtils工具類實現增,刪,改,查的操作
 */
public class DBUtilsDemo {

    @Test
    public void test() throws SQLException {
        //1.創建操作對象
        QueryRunner queryRunner = new QueryRunner();
        //2.增,刪,改是一個方法
        /*
            update(Connection conn, String sql, Object param)
            conn : 連接對象
            sql : sql語句
            param : 給占位符賦值的內容
         */
        String sql = "insert into student(sid,sname,sage) values(?,?,?)";
        //返回值 :有幾條數據受到影響
        int i = queryRunner.update(JDBCUtils.getConnection(),
                sql, 10, "kongkong", 18);
        System.out.println("有" + i + "條數據受到影響");
    }

    @Test
    public void test2() throws SQLException {
        QueryRunner queryRunner = new QueryRunner();
        /*
        query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)
        conn : 連接對象
        sql : sql語句

         */
        String sql = "select sid a,sname,sage from student where sid=?";
        //註意:類中的屬性名一定要和字段名相同。如果不相同則需要在sql語句中使用別名
//        Student student = queryRunner.query(JDBCUtils.getConnection(), sql,
//                new BeanHandler<Student>(Student.class), 10);




        sql = "select sid a,sname,sage from student";
        List<Student> list = queryRunner.query(JDBCUtils.getConnection(), sql,
                new BeanListHandler<Student>(Student.class));

       for (Student s : list) {
           System.out.println(s);
       }
    }

}

使用批處理

當我們需要對進行大批量的數據操作時,可以采用批處理技術,很簡單,在url中添加批處理的參數

jdbc:mysql://localhost:3306/Demo?rewriteBatchedStatements=true

示例代碼,(咳咳,依舊是我那個JDBCUtils實現連接的工具類…)

package com.company.jdbc3;

import com.company.jdbc.JDBCUtils;
import org.junit.Test;

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

/*
    批處理:
 */
public class BatchDemo {

    /*
        使用批處理:
            1.mysql驅動包的版本必須為5.1.3x  才支持批處理
            2.在url連接中添加如下參數
                    jdbc:mysql://localhost:3306/Demo?rewriteBatchedStatements=true
            3.使用一些API:
                //添加到批處理中
                ps.addBatch();
                //執行批處理
                ps.executeBatch();
                //清空批處理
                ps.clearBatch();
     */
    @Test
    public void test2() throws SQLException {
        //1.獲取數據庫連接
        Connection connection = JDBCUtils.getConnection();
        //2.預編譯
        PreparedStatement ps = connection.prepareStatement(
                "insert into student(sid,sname,sage) values(?,?,?)");
        //3.給占位符賦值
        for (int i = 1; i <= 100000 ; i++) {
            ps.setInt(1,i);
            ps.setString(2,"aaa"+i);
            ps.setInt(3,i);
            //添加到批處理中
            ps.addBatch();

            if (i % 1000 == 0){
                //執行sql
                ps.executeBatch();//執行批處理
                //清空批處理
                ps.clearBatch();
            }

        }
        //4.關資源
        JDBCUtils.close(ps,connection);
    }

    @Test
    public void test() throws SQLException {
        //1.獲取數據庫連接
        Connection connection = JDBCUtils.getConnection();
        //2.預編譯
        PreparedStatement ps = connection.prepareStatement(
                "insert into student(sid,sname,sage) values(?,?,?)");
        //3.給占位符賦值
        for (int i = 1; i <= 100000 ; i++) {
            ps.setInt(1,i);
            ps.setString(2,"aaa"+i);
            ps.setInt(3,i);
            //執行sql
            ps.executeUpdate();
        }
        //4.關資源
        JDBCUtils.close(ps,connection);
    }
}

到此這篇關於JDBC的擴展知識點總結的文章就介紹到這瞭,更多相關JDBC知識點內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: