JDBC事务锁基础教程
通过本教程学习JDBC基础知识以及事务机制和死锁,假设你已经学会基础数据库连接。
所谓事务transaction也就是一系列操作如同一个操作一样,要么这一系列操作全部成功,要么全部失败,不能存在有部分成功或部分不成功的情况,数据库通过ACID提供这种事务机制。参考:什么是数据库ACID
事务操作
当我们获得一个数据库连接时,其默认是处于自动提交autocommit模式,也就是意味着你发往数据库的每条语句将会立即被自动执行,这种执行有自己的事务(ACID),每条语句都彼此隔离独立的。
为了体验数据库的这种事务,我们不使用缺省的自动提交模式,当我们启动一个事务时,调用数据库连接的setAutocommit(false)方法,就如同我们以connection.startTx()启动事务一样:
import org.junit.Before; import java.sql.Connection; /** @Before @Test // 下面三个语句发给数据库了,但是没有被 // 下面是确认提交你的事务 System.out.println("Commit worked! Now everything is in the " + } catch (SQLException e) {
private void createTables(Connection conn) { } |
ROLLBACK回滚
当我们将语句发送给数据库时,我们突然不希望数据库执行这些语句,怎么办?我们可以使用回滚,使用
connection.rollback();
替代上面的:
connection.commit();
多用户与死锁
Web应用情况下一般可能同时有多个用户操作数据库同一条记录,你会有多个数据库连接。多个数据库连接会导致堵塞或死锁的情况发生,当一个用户A执行将数据放入数据表中,而另外一个用户B也许会堵塞等待用户A的更新操作完成,下面我们来模拟这种情况:
import org.junit.Before; import java.sql.Connection;
/** @Before
@Test try (Connection connectionFromHabibMarwan = getConnection()) { } //上面两个操作语句是插入name字符串是一样相同的记录
private void createTables(Connection conn) { //这里我们将数据表的items的name作为唯一字符串,不能重复
private Connection getConnection() throws SQLException { } |
上述代码我们是模拟两个用户同时操作,并不是真正打开两个数据库连接,而是打开一个数据库连接,然后开始一个事务,然后在关闭这个连接之前再打开一个连接事务,通过这种方式模拟两个用户两个事务操作同一个数据表的情况。
上面代码会抛出JdbcSQLException 错误,因为 H2 数据库发现了一个死锁,并抛出这个exception。
如果上面这种嵌套打开数据库连接不能真实模拟用户并发操作数据库的情况,我们使用两个线程来模拟并发操作,第一个连接等待1300ms提交,第二个连接至少堵塞等待1300ms。
import org.junit.Before; import java.sql.Connection; import static org.junit.Assert.assertTrue;
/** @Before private static final Long WAIT_BEFORE_COMMIT_MS = 1300l; @Test Thread i1 = new Thread(new Inserter("Jack Bauer",WAIT_BEFORE_COMMIT_MS)); Thread i2 = new Thread(new Inserter("Habib Marwan")); i1.start(); i1.join(); System.out.println("Yes!");
public class Inserter implements Runnable { private String name;
public Inserter(String name) { public Inserter(String name, Long waitBeforeCommit) { @Override try (Connection connection = getConnection()) { if (waitBeforeCommit != null) {
private void createTables(Connection conn) { // simply what we did in OpenConnectionExerciseJava6/7.java
} |
上述代码执行结果:
As you can see in the console window, both connections tried to insert something in the database. Jack Bauer 成功插入了数据到数据库,整个查询循环花费了1300ms. 而Habib Marwan 失败了,他被堵塞等待超过了1秒,而此时Jack Bauer并没有确认提交commit. 只有Jack确认提交以后, Habib 才会得到一个错误23505的信息。
下一步我们看看数据库JDBC的悲观锁与乐观锁。
什么是数据库ACID
JDBC基础教程
Hibernate专题