目录
- JDBC事务相关方法简介
- 禁用自动提交模式
- 提交事务
- 回滚事务
- PostgreSQL JDBC 事务示例
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; | |
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.*; | |
class JdbcTrasationTests { | |
private final String url = "jdbc:pspy:postgresql://localhost:5432/postgres"; | |
private final String user = "postgres"; | |
private final String password = ""; | |
/** | |
* 连接PostgreSql数据库 | |
* | |
* @return Connection | |
* @throws SQLException | |
*/ | |
public Connection connect() throws SQLException { | |
return DriverManager.getConnection(url, user, password); | |
} | |
void testTrasation() { | |
ProRank proRank = new ProRank(); | |
proRank.setLine("Mid"); | |
proRank.setName("Faker"); | |
proRank.setTeam("T"); | |
proRank.setRank(); | |
//调用 | |
addProAndUpdateRank(proRank,); | |
} | |
/** | |
* 关闭一个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 pstmt = 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 =; | |
try { | |
// 链接数据库 | |
conn = connect(); | |
conn.setAutoCommit(false); | |
// 插入一条数据 | |
pstmt = conn.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS); | |
pstmt.setString(, proRank.getName()); | |
pstmt.setString(, proRank.getTeam()); | |
pstmt.setString(, proRank.getLine()); | |
pstmt.setInt(, proRank.getRank()); | |
int affectedRows = pstmt.executeUpdate(); | |
// 判断是否生效 | |
if (affectedRows >) { | |
// 获取返回的id | |
rs = pstmt.getGeneratedKeys(); | |
if (rs.next()) { | |
id = rs.getInt(); | |
if (id >) { | |
pstmt = conn.prepareStatement(SQLUpdateRank); | |
pstmt.setInt(2, id); | |
pstmt.setInt(1, rank); | |
pstmt.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(pstmt);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 OFFSET 0;
现在,让我们测试一下事务回滚的情况,比如,我们可以在插入一条数据的时候,将name字段赋值为超出数据库长度的字串,运行程序结果如下:
ERROR: value too long for type character varying(7)
事务将回滚,并且没有任何内容插入pro_rank表