JDBC 基礎(chǔ)(下)事務(wù)控制

事務(wù)控制.png

本篇文章主要介紹的是 MySQL / JDBC 中的事務(wù)瑞你,為了方便讀者瀏覽酪惭,這里默認(rèn)需要讀者已經(jīng)掌握 SQL基礎(chǔ) 以及 JDBC 數(shù)據(jù)庫連接基礎(chǔ)。這部分的基礎(chǔ)也可以參考下面的鏈接進(jìn)行簡單的快速入門者甲。

1.概述

MySQL 事務(wù)主要用于處理操作量大春感,復(fù)雜度高的數(shù)據(jù)。比如說虏缸,在銀行轉(zhuǎn)賬系統(tǒng)中鲫懒,A -> B 轉(zhuǎn)賬 1000 元,這時(shí)就需要將 A 賬戶余額 -1000刽辙,對應(yīng)的 B 賬戶余額 +1000窥岩。這兩個(gè)操作過程必須同時(shí)執(zhí)行成功才能完成此操作,這樣扫倡,這些數(shù)據(jù)庫操作語句就構(gòu)成了一個(gè)事務(wù)谦秧。

  • 在上面的舉例中如果 A 的賬戶余額 -1000 執(zhí)行完畢后竟纳,程序被中斷了(拋出異常、服務(wù)器宕機(jī)等)疚鲤,而 B 賬戶沒有 +1000 元锥累,這肯定是有問題的。
  • 現(xiàn)在對事務(wù)應(yīng)該有一個(gè)了解了吧??事務(wù)中的多個(gè)操作集歇,或者全部執(zhí)行完畢桶略,或者全不執(zhí)行淘讥,不存在只執(zhí)行了一部分的情況掏婶。

2.事務(wù)的四大特性(ACID)

  • 原子性(Atomicity):事務(wù)中的所有操作是不可再分割的原子單位贯卦,事務(wù)中的所有操作是一個(gè)整體内狗,或者整體執(zhí)行成功耍共,亦或者整體執(zhí)行失敗砾隅。
  • 一致性(Consistency):事務(wù)執(zhí)行后储狭,數(shù)據(jù)庫狀態(tài)與其他業(yè)務(wù)規(guī)則保持一致熔掺。如轉(zhuǎn)賬業(yè)務(wù)纺荧,無論執(zhí)行成功與否旭愧,參與轉(zhuǎn)賬的兩個(gè)帳號余額值和應(yīng)該是不變的。
  • 隔離性(Isolation):在并發(fā)操作中宙暇,不同事務(wù)之間應(yīng)該隔離開來输枯,每個(gè)并發(fā)中的事務(wù)的執(zhí)行不會相互干擾。
  • 持久性(Durability):一旦事務(wù)提交成功占贫,事務(wù)中的所有數(shù)據(jù)更新必須被持久化到數(shù)據(jù)庫中桃熄,即使提交事務(wù)后,數(shù)據(jù)庫馬上崩潰型奥,在數(shù)據(jù)庫重新啟動時(shí)瞳收,也必須能保證通過某種機(jī)制恢復(fù)數(shù)據(jù)。

3.MySQL 中的事務(wù)

在默認(rèn)情況下桩引,MySQL 每執(zhí)行一條 SQL 語句缎讼,都是一個(gè)單獨(dú)的事務(wù)。如果需要在每一個(gè)事務(wù)中包含多條 SQL 語句的執(zhí)行坑匠,那么就需要開啟事務(wù)和結(jié)束事務(wù)血崭。

  • 開啟事務(wù):START TRANSACTION
  • 結(jié)束事務(wù):COMMITROLLBACK
  • 在執(zhí)行 SQL 語句之前,先執(zhí)行 START TRANSACTION 厘灼,則代表開啟了一個(gè)事務(wù)夹纫,然后執(zhí)行多條 SQL 語句,最后需要結(jié)束事務(wù)设凹,COMMIT 表示提交舰讹,即事務(wù)中的多條 SQL 語句所更改的數(shù)據(jù)會持久化到數(shù)據(jù)庫中∩林欤或者 ROLLBACK 表示回滾月匣,即回滾到事務(wù)的起點(diǎn)钻洒,將之前所做的所有操作撤銷。

Reiminder ???♂?
ROLLBACK 可以結(jié)束事務(wù)锄开,但不代表會將數(shù)據(jù)持久化到數(shù)據(jù)庫中素标,而只有 COMMIT 提交才可以將數(shù)據(jù)持久化到數(shù)據(jù)庫中。

  • 測試表:
# 創(chuàng)建 Account 表
CREATE TABLE `Account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `balance` decimal(10,0) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `Account_id_uindex` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

# 插入數(shù)據(jù)
INSERT INTO Account (name, balance) VALUES ('A', 10000);
INSERT INTO Account (name, balance) VALUES ('B', 10000);
INSERT INTO Account (name, balance) VALUES ('C', 10000);

3.1 - COMMIT 測試

# 開啟事務(wù)
START TRANSACTION;

# 執(zhí)行事務(wù) SQL 語句
# SQL1
UPDATE Account
SET balance = balance - 1000
WHERE id = 1;
# SQL2
UPDATE Account
SET balance = balance + 1000
WHERE id = 2;

# 提交事務(wù)
COMMIT;

# 結(jié)果分析
1   A   9000
2   B   11000
3   C   10000

分析:提交事務(wù)后萍悴,更新的數(shù)據(jù)將被持久化到數(shù)據(jù)庫中头遭。

3.2 - ROOLBACK 測試

# 開啟事務(wù)
START TRANSACTION;

# 執(zhí)行事務(wù) SQL 語句
# SQL1
UPDATE Account
SET balance = balance - 1000
WHERE id = 1;
# SQL2
UPDATE Account
SET balance = balance + 1000
WHERE id = 2;

# 回滾事務(wù)
ROLLBACK;
# 提交事務(wù)
COMMIT;

# 結(jié)果分析
1   A   10000
2   B   10000
3   C   10000

分析:事務(wù)提交前執(zhí)行 ROLLBACK 回滾事務(wù)至 START TRANSACTION 時(shí)的狀態(tài),所以持久化后數(shù)據(jù)庫中數(shù)據(jù)沒有被改變癣诱。

3.3 - 事務(wù)不提交測試

# 開啟事務(wù)
START TRANSACTION;

# 執(zhí)行事務(wù) SQL 語句
# SQL1
UPDATE Account
SET balance = balance - 1000
WHERE id = 1;
# SQL2
UPDATE Account
SET balance = balance + 1000
WHERE id = 2;

# 輸出結(jié)果
SELECT *
FROM Account;

# 控制臺打印數(shù)據(jù)
1   A   9000
2   B   11000
3   C   10000

# 結(jié)果分析(數(shù)據(jù)庫數(shù)據(jù))
1   A   10000
2   B   10000
3   C   10000

分析:在執(zhí)行了 SQL 語句后计维,在內(nèi)存中的數(shù)據(jù)表數(shù)據(jù)已經(jīng)被修改了,但是由于沒有提交事務(wù)撕予,所以數(shù)據(jù)沒有被持久化到數(shù)據(jù)庫中鲫惶。

4.并發(fā)事務(wù)問題

  • 臟讀(Dirty Read):在事務(wù)的執(zhí)行過程中,讀取到了其他事務(wù)的 未提交 的數(shù)據(jù)实抡,即讀到了臟數(shù)據(jù)剑按。
  • 不可重復(fù)讀(Unrepeatable Read):在事務(wù)的執(zhí)行過程中,讀到了其他事務(wù) 修改后 的數(shù)據(jù)澜术,換句話說在該事務(wù)中的不同時(shí)間點(diǎn)讀取到了不一致的數(shù)據(jù),即不可重復(fù)讀猬腰。
  • 幻讀/虛讀(Phantom Read):在事務(wù)的執(zhí)行過程中鸟废,讀取到了其他事務(wù)對 記錄數(shù) 修改后的數(shù)據(jù),對同一張表的兩次查詢的 COUNT(*) 不一致姑荷。
  • 不可重復(fù)讀與幻讀的區(qū)別:
    • 不可重復(fù)讀:強(qiáng)調(diào)的是數(shù)據(jù) 內(nèi)容 的不一致盒延,主要針對 UPDATE 的修改。
    • 幻讀:強(qiáng)調(diào)的是 記錄數(shù) 的不一致鼠冕,主要針對 INSERT/DELETE 的修改添寺。

5.四大隔離級別

剛剛我們介紹了事務(wù)并發(fā)時(shí)可能出現(xiàn)的各種問題,其實(shí)可以發(fā)現(xiàn)是違背了事務(wù)的 隔離性 的要求所引起的懈费,所以我們需要通過事務(wù)的隔離來解決這個(gè)問題计露,下面我們就來介紹一下事務(wù)的四大隔離級別。

  • 串行化(SERIALIZABLE):
    • 概述:對數(shù)據(jù)串行的訪問憎乙,非并發(fā)訪問票罐。
    • 特點(diǎn):不會出現(xiàn)任何并發(fā)問題,性能最差泞边。
    • 理解:在當(dāng)前串行化事務(wù)中该押,如果有其他事務(wù)對數(shù)據(jù)進(jìn)行了增刪改操作,當(dāng)前事務(wù)讀取數(shù)據(jù)會被阻塞阵谚,需要等到其他事務(wù)結(jié)束后(ROLLBACK/COMMIT)才能執(zhí)行數(shù)據(jù)讀取蚕礼。
  • 可重復(fù)讀(REPEATABLE READ):
    • 概述:在一個(gè)事務(wù)的執(zhí)行過程中烟具,能保證讀取到數(shù)據(jù)的一致性,是 MySQL 中使用的 InnoDB 存儲引擎默認(rèn)的隔離級別奠蹬。
    • 特點(diǎn):可避免臟讀和不可重復(fù)讀朝聋,不能避免幻讀問題,并發(fā)式讀取訪問罩润,性能比串行化好玖翅。
    • 理解:在一個(gè)事務(wù)內(nèi),鎖定讀取割以,通過保存第一次讀取的快照(snapshot)金度,保證每次讀取的數(shù)據(jù)一致。
  • 讀已提交(READ COMMITTED):
    • 概述:在一個(gè)事務(wù)內(nèi)严沥,可以讀取到其他事務(wù)已經(jīng)提交的數(shù)據(jù)猜极,性能優(yōu)于可重復(fù)讀(REPEATABLE READ),Oracle 數(shù)據(jù)庫中的默認(rèn)隔離級別消玄。
    • 特點(diǎn):可避免臟讀跟伏,不能避免不可重復(fù)讀和幻讀問題,并發(fā)式訪問讀取翩瓜,性能比可重復(fù)讀好受扳。
    • 理解:MySQL中與 Oracle 的該隔離級別,通過讀取新鮮的快照(fresh snapshot)來讀取其他事務(wù)已提交的更新內(nèi)容兔跌。
  • 讀未提交(READ UNCOMMITTED):
    • 概述:在一個(gè)事務(wù)內(nèi)勘高,可以讀取到其他事務(wù)沒有提交的修改內(nèi)容,即臟讀(Dirty Read)坟桅。
    • 特點(diǎn):不能避免任何并發(fā)事務(wù)的問題华望,性能最好。
    • 理解:在 SERIALIZBLE 的事務(wù)隔離級別仅乓,InnoDB 存儲引擎會對每個(gè) SELECT 語句后自動加上 LOCK IN SHARE MODE赖舟,即給每個(gè)讀取操作加一個(gè)共享鎖,因此在這個(gè)事務(wù)隔離級別下夸楣,讀占用鎖了宾抓,一致性的非鎖定讀不再予以支持,一般不會在本地事務(wù)中使用 SERIALIZBLE 的隔離級別豫喧,SERIALIZABLE 的事務(wù)隔離級別主要用于 InnoDB 存儲引擎的分布式事務(wù)洞慎。
隔離級別 臟讀 不可重復(fù)讀 幻讀
串行化(SERIALIZABLE) ?? ?? ??
可重復(fù)讀(REPEATABLE READ) ?? ?? -
讀已提交(READ COMMITTED) ?? ? ?
讀未提交(READ UNCOMMITTED) ? ? ?

6.MySQL 各隔離級別的并發(fā)事務(wù)測試

  • 查看隔離級別:MySQL 默認(rèn)隔離級別是 REPEATABLE-READ,可以通過 SELECT @@TX_ISOLATION; 查看隔離級別嘿棘。

  • 設(shè)置隔離級別:SET SESSION TRANSACTION ISOLATION LEVEL xxx;

  • 測試表及數(shù)據(jù)

id name balance
1 A 10000
2 B 10000

6.1 - 串行化測試

  • 測試版本:MySQL Server 5.7
  • 測試環(huán)境:
# 設(shè)置窗口 2 隔離級別為 串行化
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 特別的 -> 窗口 1 的隔離級別不需要特別設(shè)置劲腿。
# 我們演示是通過窗口 1 進(jìn)行修改數(shù)據(jù)值,在窗口 2 來觀察結(jié)果的鸟妙。
串行化測試

值得注意??在第 3 步焦人,窗口 1 執(zhí)行 INSERT 插入了一條數(shù)據(jù)挥吵,而后第 4 步窗口 2 執(zhí)行 SELECT 操作會被阻塞(避免幻讀),直到窗口 1 事務(wù)結(jié)束(COLLBACK/COMMIT)后才會被執(zhí)行花椭。
特別的??當(dāng)窗口 2 一旦執(zhí)行過 SELECT 操作后忽匈,如果有其他事務(wù)對數(shù)據(jù)進(jìn)行增刪改操作都將被阻塞(可重復(fù)讀的保證),直到該串行化事務(wù)結(jié)束后才會被執(zhí)行矿辽。

6.2 - 可重復(fù)讀測試

可重復(fù)讀測試

與串行化類似的是丹允,當(dāng)窗口 2 執(zhí)行步驟 3 讀操作后,查詢的結(jié)果將被鎖定袋倔。當(dāng)其他事務(wù)要對該鎖定數(shù)據(jù)執(zhí)行更改操作時(shí)都將會被阻塞雕蔽,所以當(dāng)窗口 1 執(zhí)行步驟 4 時(shí)將會被阻塞,從而保證了可重復(fù)讀宾娜。

6.3 - 讀已提交測試

讀已提交測試

當(dāng)步驟 4 修改了 balance 值時(shí)批狐,此時(shí)還未提交,所以步驟 5 查詢到的結(jié)果并沒有改變(讀已提交)前塔,而在步驟 7 查詢到了窗口 1 改變的結(jié)果嚣艇,因?yàn)榇藭r(shí)窗口 1 的事務(wù)已經(jīng)提交。
特別的??在窗口 2 事務(wù)的執(zhí)行過程中华弓,步驟 3 與步驟 7 查詢到了不同的結(jié)果食零,由此可以看出這是與可重復(fù)讀的重要區(qū)別。

6.4 - 讀未提交測試

讀未提交測試

步驟 4 中窗口 1 事務(wù)修改了數(shù)據(jù)寂屏,步驟 5 中窗口 2 事務(wù)讀取到了修改后的數(shù)據(jù)慌洪,此時(shí)窗口 1 事務(wù)還未提交,因此讀取到的是 臟數(shù)據(jù)凑保,該隔離級別不能避免任何的并發(fā)事務(wù)問題。

7.JDBC 事務(wù)

剛剛我們介紹了在 MySQL 中對事務(wù)進(jìn)行的操作涌攻,而 JDBC 中 也必然有與對應(yīng)的方式進(jìn)行事務(wù)控制欧引,下面我們介紹一下 JDBC 中對事務(wù)的控制。

  • 在 JDBC 中處理事務(wù)都是通過 Connection 完成的恳谎。
  • 同一個(gè)事務(wù)中的所有的操作芝此,都是使用同一個(gè) Connection 對象。

7.1 - 開啟事務(wù)

  • 方法:void setAutoCommit(boolean autoCommit)

讀讀 API ??
If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either the method commit or the method rollback. By default, new connections are in auto-commit mode.

  • 如果 connection 處于自動提交模式因痛,會將每一條 SQL 語句作為一個(gè)單獨(dú)的事務(wù)提交(commit)婚苹;否則,其 SQL 語句可以通過調(diào)用 commit() 方法或 rollback() 方法終止事務(wù)鸵膏。默認(rèn)是自動提交模式膊升。

Reminder ???♂?
Java 還特別指出:對于 DML 語句,例如插入谭企、 更新或刪除和 DDL 語句廓译,該語句是完整的盡快它執(zhí)行完评肆。
Select 語句,該語句完成時(shí)關(guān)閉關(guān)聯(lián)的 ResultSet非区。

7.2 - 提交事務(wù)

  • 方法:commit()

讀讀 API ??
Makes all changes made since the previous commit/rollback permanent and releases any database locks currently held by this Connection object. This method should be used only when auto-commit mode has been disabled.

  • 提交自上次提交后的所有更改瓜挽,并釋放目前此連接對象的任何 數(shù)據(jù)庫鎖
  • 只有當(dāng)禁用了自動提交時(shí)此方法有效征绸。

7.3 - 回滾事務(wù)

  • 方法:rollback()

讀讀 API ??
Undoes all changes made in the current transaction and releases any database locks currently held by this Connection object. This method should be used only when auto-commit mode has been disabled.

  • 撤銷對當(dāng)前事務(wù)中所做的所有更改久橙,并釋放目前此連接對象持有的任何 數(shù)據(jù)庫鎖
  • 只有當(dāng)禁用了自動提交時(shí)此方法有效管怠。

7.4 - 設(shè)置保存點(diǎn)

  • 方法:Savepoint setSavepoint(String name)

讀讀 API ??
Creates a savepoint with the given name in the current transaction and returns the new Savepoint object that represents it.
if setSavepoint is invoked outside of an active transaction, a transaction will be started at this newly created savepoint.

  • 在當(dāng)前事務(wù)中創(chuàng)建一個(gè)指定名稱的保存點(diǎn)淆衷,并返回一個(gè)用來表示它的新的保存點(diǎn)對象。
  • 如果該方法在一個(gè)事務(wù)外被調(diào)用時(shí)排惨,將在這個(gè)新創(chuàng)建的保存點(diǎn)啟動事務(wù)吭敢。

7.5 - 事務(wù)回滾

  • 不帶保存點(diǎn)的 JDBC 事務(wù)的基本格式:
try {
    connection.setAutoCommit(false);    // 禁用自動提交
    ...
    ...
    connection.commit();    // 在 try 的末尾提交
} catch() {
    connection.rollback();  // 事務(wù)執(zhí)行中斷則回滾
}
  • 代碼示例:
public static void transfer(boolean b) throws Throwable {
    Connection connection = null;
    PreparedStatement preparedStatement = null;

    try {
        connection = JdbcUtils.getConnection();
        // 禁用自動提交
        connection.setAutoCommit(false);

        String sql = "UPDATE Account SET balance = balance + ? WHERE id = ?";
        preparedStatement = connection.prepareStatement(sql);

        // 操作 1
        preparedStatement.setDouble(1, -10000);
        preparedStatement.setInt(2, 1);
        preparedStatement.executeUpdate();

        // 在事務(wù)的兩個(gè)操作中拋出異常,中斷事務(wù)內(nèi)務(wù)的執(zhí)行
        if (b) {
            throw new Exception();
        }

        // 操作 2
        preparedStatement.setDouble(1, 10000);
        preparedStatement.setInt(2, 2);
        preparedStatement.executeUpdate();

        // 提交事務(wù)
        connection.commit();
    } catch (Exception e) {
        try {
            if (connection != null) {
                connection.rollback();
            }
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        throw new RuntimeException();
    } finally {
        JdbcUtils.release(connection, preparedStatement);
    }
}

7.6 - 回滾到保存點(diǎn)

  • 概述:保存點(diǎn)(savePoint) 是 JDBC 3.0 的 API暮芭,其要求數(shù)據(jù)庫支持以保存點(diǎn)方式的的回滾鹿驼。
  • 檢查方法:boolean b = connection.getMetaData().supportsSavepoints();
  • 回滾到保存點(diǎn)方法:void rollback(Savepoint savepoint)
  • 作用:保存點(diǎn)的作用是將事務(wù)回滾到指定的保存點(diǎn)。需要在事務(wù)中先設(shè)置好保存點(diǎn)辕宏,然后回滾時(shí)通過 Savepoint 回滾到指定的保存點(diǎn)畜晰,而不是回滾整個(gè)事務(wù)。

Reminder ???♂?
回滾到指定的保存點(diǎn)并沒有結(jié)束事務(wù)瑞筐,只有回滾了整個(gè)事務(wù)才會結(jié)束事務(wù)凄鼻。

  • 代碼示例:
    /*
     * 李四對張三說,如果你給我轉(zhuǎn)1W聚假,我就給你轉(zhuǎn)100W块蚌。
     * ==========================================
     * 
     * 張三給李四轉(zhuǎn)1W(張三減去1W,李四加上1W)
     * 設(shè)置保存點(diǎn)膘格!
     * 李四給張三轉(zhuǎn)100W(李四減去100W峭范,張三加上100W)
     * 查看李四余額為負(fù)數(shù),那么回滾到保存點(diǎn)瘪贱。
     * 提交事務(wù)
     */
private static void savepoint() throws RuntimeException {
    Connection connection = null;
    PreparedStatement preparedStatement = null;

    try {
        connection = JdbcUtils.getConnection();
        // 禁用自動提交
        connection.setAutoCommit(false);

        String sql = "UPDATE Account SET balance = balance + ? WHERE name = ?";
        preparedStatement = connection.prepareStatement(sql);

        // 操作1(張三減去1W)
        preparedStatement.setDouble(1, -10000);
        preparedStatement.setString(2, "zs");
        preparedStatement.executeUpdate();

        // 操作2(李四加上1W)
        preparedStatement.setDouble(1, 10000);
        preparedStatement.setString(2, "ls");
        preparedStatement.executeUpdate();

        // 設(shè)置表存點(diǎn)
        Savepoint savepoint = connection.setSavepoint();

        // 操作3(李四減去100W)
        preparedStatement.setDouble(1, -1000000);
        preparedStatement.setString(2, "ls");
        preparedStatement.executeUpdate();

        // 操作4(張三加上100W)
        preparedStatement.setDouble(1, 1000000);
        preparedStatement.setString(2, "zs");
        preparedStatement.executeUpdate();

        // 操作5(查看李四余額)
        sql = "SELECT balance FROM Account WHERE name = ?";
        preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1, "ls");
        ResultSet resultSet = preparedStatement.executeQuery();
        double balance = 0;
        if (resultSet.next()) {
            balance = resultSet.getDouble("balance");
        }

        // 如果李四的余額為負(fù)數(shù)纱控,那么回滾到指定保存點(diǎn)
        if (balance < 0) {
            connection.rollback(savepoint);
            System.out.println("張三你上當(dāng)了");
        }

        // 提交事務(wù)
        connection.commit();

    } catch (SQLException e) {
        // 回滾事務(wù)
        if (connection != null) {
            try {
                connection.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }
        throw new RuntimeException();
    } finally {
        JdbcUtils.release(connection, preparedStatement);
    }
}

悄悄話 ??

  • 年后的學(xué)習(xí)節(jié)奏變得非常之快,導(dǎo)致最近也很久沒有與大家分享技術(shù)筆記了菜秦,最近學(xué)習(xí)了 JavaWeb 的 HTML甜害、CSS、JS球昨、MySQL尔店、Tomcat、Servlet、JSP 等等內(nèi)容闹获,哪一個(gè)技術(shù)拿出來都應(yīng)該可以讓我來研究一陣子了期犬,怎奈進(jìn)度太快也只能是抓大放小。最近在數(shù)據(jù)庫階段的事務(wù)控制的部分我比較感興趣并做了一些小實(shí)驗(yàn)避诽,覺得有一些意義龟虎,所以來與大家分享一下。
  • 后面的 Cookie沙庐、Session 技術(shù)也是我覺得理解的比較深入的一個(gè)技術(shù)點(diǎn)鲤妥,我會留在下次的更新中進(jìn)行分享。

彩蛋 ??

  • 最近在整理一些 JavaWeb 成長之路 的一些學(xué)習(xí)筆記拱雏,本篇是 Database 系列中的一篇棉安,今后還會與大家分享 JavaWeb 中的一系列的技術(shù),有興趣的朋友可以關(guān)注我的專題铸抑,一同學(xué)習(xí)贡耽。

如果你覺得我的分享對你有幫助的話,請?jiān)谙旅??隨手點(diǎn)個(gè)喜歡 ??鹊汛,你的肯定才是我最大的動力蒲赂,感謝。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刁憋,一起剝皮案震驚了整個(gè)濱河市滥嘴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌至耻,老刑警劉巖若皱,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異尘颓,居然都是意外死亡走触,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門疤苹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來互广,“玉大人,你說我怎么就攤上這事痰催。” “怎么了迎瞧?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵夸溶,是天一觀的道長。 經(jīng)常有香客問我凶硅,道長缝裁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮捷绑,結(jié)果婚禮上韩脑,老公的妹妹穿的比我還像新娘。我一直安慰自己粹污,他們只是感情好段多,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著壮吩,像睡著了一般进苍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鸭叙,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天觉啊,我揣著相機(jī)與錄音,去河邊找鬼沈贝。 笑死杠人,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宋下。 我是一名探鬼主播嗡善,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼杨凑!你這毒婦竟也來了滤奈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤撩满,失蹤者是張志新(化名)和其女友劉穎蜒程,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伺帘,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昭躺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伪嫁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片领炫。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖张咳,靈堂內(nèi)的尸體忽然破棺而出帝洪,到底是詐尸還是另有隱情,我是刑警寧澤脚猾,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布葱峡,位于F島的核電站,受9級特大地震影響龙助,放射性物質(zhì)發(fā)生泄漏砰奕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望军援。 院中可真熱鬧仅淑,春花似錦、人聲如沸胸哥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烘嘱。三九已至昆禽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蝇庭,已是汗流浹背醉鳖。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哮内,地道東北人盗棵。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像北发,于是被迫代替她去往敵國和親纹因。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容