MySQL的事務

来源:https://www.cnblogs.com/xuyiqing/archive/2018/02/08/8430214.html
-Advertisement-
Play Games

一、事務概述 1.什麼是事務 一件事情有n個組成單元 要不這n個組成單元同時成功 要不n個單元就同時失敗 就是將n個組成單元放到一個事務中 2.mysql的事務 預設的事務:一條sql語句就是一個事務 預設就開啟事務並提交事務 手動事務: 1)顯示的開啟一個事務:start transaction ...


一、事務概述

1.什麼是事務

一件事情有n個組成單元 要不這n個組成單元同時成功 要不n個單元就同時失敗

就是將n個組成單元放到一個事務中

2.mysql的事務

預設的事務:一條sql語句就是一個事務 預設就開啟事務並提交事務

手動事務:

1)顯示的開啟一個事務:start transaction

2)事務提交:commit代表從開啟事務到事務提交 中間的所有的sql都認為有效      真正的更新資料庫

3)事務的回滾:rollback 代表事務的回滾 從開啟事務到事務回滾 中間的所有的 sql操作都認為無效資料庫沒有被更新

 

二、JDBC事務操作

預設是自動事務:

執行sql語句:executeUpdate()  ---- 每執行一次executeUpdate方法 代表   事務自動提交

通過jdbc的API手動事務:

開啟事務:conn.setAutoComnmit(false);

提交事務:conn.commit();

回滾事務:conn.rollback();

註意:控制事務的connnection必須是同一個

執行sql的connection與開啟事務的connnection必須是同一個才能對事務進行控制

 

示例:

package jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCDemo {
    public static void main(String[] args) {
        Connection conn = null;
        try {
            // 註冊驅動
            Class.forName("com.mysql.jdbc.Driver");
            // 獲得連接
            conn = DriverManager.getConnection("jdbc:mysql:///web", "root", "xuyiqing");
            //開啟事務
            conn.setAutoCommit(false);//false表示不自動提交
            // 獲得執行平臺
            Statement stmt = conn.createStatement();            
            // 執行sql
            stmt.executeUpdate("update user set password=123456 where username=zhangsan");
            //提交事務
            conn.commit();
            
            stmt.close();
            conn.close();
        } catch (Exception e) {
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }
}
View Code

 

三、DBUtils事務操作

1.QueryRunner

有參構造:QueryRunner runner = new QueryRunner(DataSource dataSource);

有參構造將數據源(連接池)作為參數傳入QueryRunner,QueryRunner會從連   接池中獲得一個資料庫連接資源操作資料庫,所以直接使用無Connection參數      的update方法即可操作資料庫

 

無參構造:QueryRunner runner = new QueryRunner();

無參的構造沒有將數據源(連接池)作為參數傳入QueryRunner,那麼我們在使 用QueryRunner對象操作資料庫時要使用有Connection參數的方法

 

示例:

package dbutils;

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

import org.apache.commons.dbutils.QueryRunner;

import utils.DataSourceUtils;

public class DBUtilsDemo {
    public static void main(String[] args) {
        Connection conn = null;
        try {
            // 無參構造
            QueryRunner runner = new QueryRunner();
            // 獲得連接
            conn = DataSourceUtils.getConnection();
            // 開啟事務
            conn.setAutoCommit(false);
            runner.update(conn, "update user set password=123456 where username=zhangsan");
            // 提交或回滾事務
            conn.commit();
        } catch (Exception e) {
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }
    }
}
View Code

工具類:

package utils;

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 DataSourceUtils {

    private static DataSource dataSource = new ComboPooledDataSource();

    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    // 直接可以獲取一個連接池
    public static DataSource getDataSource() {
        return dataSource;
    }
    
    public static Connection getConnection() throws SQLException{
        return dataSource.getConnection();
    }

    // 獲取連接對象
    public static Connection getCurrentConnection() throws SQLException {

        Connection con = tl.get();
        if (con == null) {
            con = dataSource.getConnection();
            tl.set(con);
        }
        return con;
    }

    // 開啟事務
    public static void startTransaction() throws SQLException {
        Connection con = getCurrentConnection();
        if (con != null) {
            con.setAutoCommit(false);
        }
    }

    // 事務回滾
    public static void rollback() throws SQLException {
        Connection con = getCurrentConnection();
        if (con != null) {
            con.rollback();
        }
    }

    // 提交並且 關閉資源及從ThreadLocall中釋放
    public static void commitAndRelease() throws SQLException {
        Connection con = getCurrentConnection();
        if (con != null) {
            con.commit(); // 事務提交
            con.close();// 關閉資源
            tl.remove();// 從線程綁定中移除
        }
    }

    // 關閉資源方法
    public static void closeConnection() throws SQLException {
        Connection con = getCurrentConnection();
        if (con != null) {
            con.close();
        }
    }

    public static void closeStatement(Statement st) throws SQLException {
        if (st != null) {
            st.close();
        }
    }

    public static void closeResultSet(ResultSet rs) throws SQLException {
        if (rs != null) {
            rs.close();
        }
    }

}
View Code

 

 

案例:簡單的轉賬代碼:

 

代碼實現:

 頁面transfer.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="${pageContext.request.contextPath }/transfer" method="post">
        轉出賬戶:<input type="text" name="out"><br>
        轉入賬戶:<input type="text" name="in"><br>
        轉賬金額:<input type="text" name="money"><br>
        <input type="submit" value="確認轉賬"><br>
    </form>
</body>
</html>
View Code

web層:

package transfer.web;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import transfer.service.TransferService;

public class TransferServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        //接受轉賬的參數
        String out = request.getParameter("out");
        String in = request.getParameter("in");
        String moneyStr = request.getParameter("money");
        double money = Double.parseDouble(moneyStr);
        
        //調用業務層的轉賬方法
        TransferService service = new TransferService();
        boolean isTransferSuccess = service.transfer(out,in,money);
        
        response.setContentType("text/html;charset=UTF-8");
        
        if(isTransferSuccess){
            response.getWriter().write("轉賬成功!!!");
        }else{
            response.getWriter().write("轉賬失敗!!!");
        }
        
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}
View Code

service層:

package transfer.service;

import java.sql.SQLException;

import transfer.dao.TransferDao;
import utils.MyDataSourceUtils;

public class TransferService {

    public boolean transfer(String out, String in, double money) {
        
        TransferDao dao = new TransferDao();
        
        boolean isTranferSuccess = true;
        try {
            //開啟事務
            MyDataSourceUtils.startTransaction();
            //轉出前的方法
            dao.out(out,money);
            //轉入錢的方法
            dao.in(in,money);
            
        } catch (Exception e) {
            isTranferSuccess = false;
            //回滾事務
            try {
                MyDataSourceUtils.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally{
            try {
                MyDataSourceUtils.commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
        return isTranferSuccess;
        
    }

}
View Code

dao層:

package transfer.dao;

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

import org.apache.commons.dbutils.QueryRunner;

import utils.MyDataSourceUtils;

public class TransferDao {

    public void out(String out, double money) throws SQLException {
        QueryRunner runner = new QueryRunner();
        Connection conn = MyDataSourceUtils.getCurrentConnection();
        String sql = "update account set money=money-? where name=?";
        runner.update(conn, sql, money, out);
    }

    public void in(String in, double money) throws SQLException {
        QueryRunner runner = new QueryRunner();
        Connection conn = MyDataSourceUtils.getCurrentConnection();
        String sql = "update account set money=money+? where name=?";
        runner.update(conn, sql, money, in);
    }

}
View Code

工具類:

package utils;

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

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class MyDataSourceUtils {

    //獲得Connection ----- 從連接池中獲取
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
    
    //創建ThreadLocal
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    
    //開啟事務
    public static void startTransaction() throws SQLException{
        Connection conn = getCurrentConnection();
        conn.setAutoCommit(false);
    }
    
    //獲得當前線程上綁定的conn
    public static Connection getCurrentConnection() throws SQLException{
        //從ThreadLocal尋找 當前線程是否有對應Connection
        Connection conn = tl.get();
        if(conn==null){
            //獲得新的connection
            conn = getConnection();
            //將conn資源綁定到ThreadLocal(map)上
            tl.set(conn);
        }
        return conn;
    }
    
    public static Connection getConnection() throws SQLException{
        return dataSource.getConnection();
    }

    //回滾事務
    public static void rollback() throws SQLException {
        getCurrentConnection().rollback();
    }

    //提交事務
    public static void commit() throws SQLException {
        Connection conn = getCurrentConnection();
        conn.commit();
        //將Connection從ThreadLocal中移除
        tl.remove();
        conn.close();
        
    }

}
View Code

註意c3p0和web的配置文件

 

 

四、事務的特性和隔離級別(概念性問題---面試)

1.事務的特性ACID

        1)原子性(Atomicity)原子性是指事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生。 

        2)一致性(Consistency)一個事務中,事務前後數據的完整性必須保持一致。

        3)隔離性(Isolation)多個事務,事務的隔離性是指多個用戶併發訪問資料庫時, 一個用戶的 事務不能被其它用戶的事務所干擾,多個併發事務之間數據要相互隔離。

        4)持久性(Durability)持久性是指一個事務一旦被提交,它對資料庫中數據的改變就是永久性的,接下來即使資料庫發生故障也不應該對其有任何影響。

 

2.併發訪問問題----由隔離性引起

如果不考慮隔離性,事務存在3中併發訪問問題。

1)臟讀:B事務讀取到了A事務尚未提交的數據   ------  要求B事務要讀取A事 務提交的數據

2)不可重覆讀:一個事務中 兩次讀取的數據的內容不一致  ----- 要求的是一個事 務中多次讀取時數據是一致的  --- unpdate

3)幻讀/虛讀:一個事務中 兩次讀取的數據的數量不一致  ----- 要求在一個事務多 次讀取的數據的數量是一致的 --insert  delete

 

3.事務的隔離級別

1)read uncommitted : 讀取尚未提交的數據 :哪個問題都不能解決

2)read committed:讀取已經提交的數據 :可以解決臟讀 ---- oracle預設的

3)repeatable read:重讀讀取:可以解決臟讀 和 不可重覆讀 ---mysql預設的

4)serializable:串列化:可以解決 臟讀 不可重覆讀 和 虛讀---相當於鎖表,效率過低不使用

 

註意:mysql資料庫預設的隔離級別

查看mysql資料庫預設的隔離級別:select @@tx_isolation

設置mysql的隔離級別:set session transaction isolation level 設置事務隔離級別

 

隔離級別的性能:read uncommitted>read committed>repeatable read>serialazable

安全性:read uncommitted<read committed<repeatable read<serialazable

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • root@test:/# dpkg -l | grep cobbler root@test:/# sudo dpkg --purge cobbler ...
  • Centos使用AD賬戶進行驗證,網上查有很多種,包括samba+winbind,sssd,nss-pam-ldapd等多種方式。今天介紹通過nss-pam-ldap驗證AD賬號。 一.實驗環境: 兩台主機:一臺windows server2012 R2 域控,一臺centos7.2客戶端使用AD賬 ...
  • 操作系統 : CentOS7.3.1611_x64 go語言版本:1.8.3 linux/amd64 InfluxDB版本:1.1.0 服務模塊介紹 源碼路徑: github.com/influxdata/influxdb/services/snapshotter service.go : snap ...
  • 學習目標 描述使用ASM的好處 管理ASM實例 創建和刪除ASM磁碟組 擴展ASM磁碟組 通過使用各種實用程式檢索ASM元數據 ASM對於管理員的好處 使用ASM可以免除: -I/O性能優化:ASM採用條帶化和鏡像所有數據的策略,且執行自動重新平衡操作。 -數據文件移動和重新組織:不再需要更改數據文 ...
  • 轉自腳本之家: 看看下麵的1.判斷是否有註入;and 1=1;and 1=22.初步判斷是否是mssql;and user>03.判斷資料庫系統;and (select count(*) from sysobjects)>0 mssql;and (select count(*) from msyso ...
  • 讀取數據如下: http://www.dofactory.com/reference/connection-strings ...
  • 偶爾對比起2016以下的版本(比如ssms2014),ssms2016有一個小地方有區別。就是報錯的行號有區別 舉個例子,下麵同樣的語句在ssms2014和ssms2016裡面運行。就是如下的效果 顯而易見,這裡的行9和 xml 變數的定義都是有問題的。所以ssms2014裡面直接給出 這樣的提示錯 ...
  • 由於 0.10.x 版 Kafka 與 0.8.x 版有很大的變化,這種變化對下游 Storm 有非常大的影響,0.10.x 版的 Kafka 不但增加了許可權管理的功能,而且還將 simple 和 high consumer 的 offsets 進行統一管理,也就意味著在 0.8.x 中 Storm ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...