日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

PostgreSql?JDBC事務操作方法詳解_PostgreSQL

作者:Zeuss ? 更新時間: 2022-12-14 編程語言

JDBC事務相關方法簡介

本文將借助示例,簡單講解下JDBC操作Pg事務的流程。

首先來簡單講解下事務的定義:為了確保兩個(多個)數據庫操作都生效,或者兩個操作都不發生,可以使用事務。根據定義,事務是作為單個單元執行的一組語句。換句話說,要么所有語句都成功執行,要么沒有執行。

禁用自動提交模式

當建立與PostgreSQL數據庫的連接時,它處于自動提交模式。這意味著每個SQL語句都被視為事務并自動提交。

如果要在事務中封裝一個或多個語句,則必須禁用自動提交模式。為此,我們可以調用Connection.setAutoCommit()方法來修改SQL提交模式:

Connection.setAutoCommit(false);

最佳做法是僅對事務模式禁用自動提交模式。它避免為多個語句保留數據庫鎖。

提交事務

要提交事務,請調用Connection對象的commit方法,如下所示:

Connection.commit();

當調用commit()方法,所有前面的SQL語句作為一個單元一起提交。

回滾事務

既然使用了事務,那我們肯定會有回滾的時候,我們可以使用rollback()方法來中止當前事務并將值恢復為原始值。

Connection.rollback();

PostgreSQL JDBC 事務示例

讓我們舉一個使用JDBC API執行PostgreSQL事務的示例。

首先,創建一個表示ProRank的實體類,如下所示:

import lombok.Data;
@Data
public class ProRank {
    Integer id;
    String name;
    String team;
    String line;
    Integer rank;
}

然后,編寫以下代碼,供我們測試事務操作。

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.sql.*;
@SpringBootTest
class JdbcTrasationTests {
    private final String url = "jdbc:p6spy:postgresql://localhost:5432/postgres";
    private final String user = "postgres";
    private final String password = "112233";
    /**
     * 連接PostgreSql數據庫
     *
     * @return Connection
     * @throws SQLException
     */
    public Connection connect() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }
    @Test
    void testTrasation() {
        ProRank proRank = new ProRank();
        proRank.setLine("Mid");
        proRank.setName("Faker");
        proRank.setTeam("T1");
        proRank.setRank(0);
        //調用
        addProAndUpdateRank(proRank,2222);
    }
    /**
     * 關閉一個AutoCloseable對象
     *
     * @param closeable
     */
    private void close(AutoCloseable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
    /**
     * 插入一條選手記錄,更新他的rank值
     *
     * @param proRank
     * @param rank
     */
    public void addProAndUpdateRank(ProRank proRank, Integer rank) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        PreparedStatement pstmt2 = null;
        ResultSet rs = null;
        // 插入一條數據
        String SQL = "INSERT INTO pro_rank(name,team,line,rank) VALUES(?,?,?,?)";
        // 更新他的rank值
        String SQLUpdateRank = "UPDATE pro_rank SET rank = ? WHERE id = ?;";
        int id = 0;
        try {
            // 鏈接數據庫
            conn = connect();
            conn.setAutoCommit(false);
            // 插入一條數據
            pstmt = conn.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS);
            pstmt.setString(1, proRank.getName());
            pstmt.setString(2, proRank.getTeam());
            pstmt.setString(3, proRank.getLine());
            pstmt.setInt(4, proRank.getRank());
            int affectedRows = pstmt.executeUpdate();
            // 判斷是否生效
            if (affectedRows > 0) {
                // 獲取返回的id
                rs = pstmt.getGeneratedKeys();
                if (rs.next()) {
                    id = rs.getInt(1);
                    if (id > 0) {
                        pstmt2 = conn.prepareStatement(SQLUpdateRank);
                        pstmt2.setInt(2, id);
                        pstmt2.setInt(1, rank);
                        pstmt2.executeUpdate();
                    }
                }
            } else {
                // 如果新增數據失敗,回滾
                conn.rollback();
            }
            // 提交事務
            conn.commit();
            System.out.println("插入選手數據成功!更新選手rank成功,數據id:" + id);
        } catch (SQLException sqlException) {
            System.out.println(sqlException.getMessage());
            sqlException.printStackTrace();
            // 回滾事務
            System.out.println("回滾事務...");
            try {
                if (conn != null) {
                    conn.rollback();
                }
            } catch (SQLException e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
        } finally {
            close(rs);close(pstmt);close(pstmt2);close(conn);
        }
    }
}

讓我們看一下上面的代碼,他包含三個方法

connect()?方法建立與數據庫連接,并返回連接對象。

close()?方法關閉數據庫操作可關閉的對象,如Resultset、Statement和Connection。

addProAndUpdateRank()方法插入新的選手,并在事務中更新選手的rank字段。此方法包含邏輯如下:

  • 首先,在pro_rank表中插入一條新的選手數據。
  • 接下來,獲取新插入的選手數據的id
  • 然后,更新插入選手的rank值。
  • 之后,如果步驟2和3均成功,則提交事務。否則,回滾事務
  • 最后,關閉ResultSet、PreparedStatement和Connection對象。

如果我們在第一個場景中執行程序,我們會得到以下結果:

插入選手數據成功!更新選手rank成功,數據id:14

我們可以通過查詢pro_rank表格,來查看上述代碼執行結果:

SELECT * FROM "public"."pro_rank" LIMIT 1000 OFFSET 0;

現在,讓我們測試一下事務回滾的情況,比如,我們可以在插入一條數據的時候,將name字段賦值為超出數據庫長度的字串,運行程序結果如下:

ERROR: value too long for type character varying(7)

事務將回滾,并且沒有任何內容插入pro_rank表

原文鏈接:https://juejin.cn/post/7166032867829481485

欄目分類
最近更新