在上一篇中我们介绍了数据库锁的相关知识,并且知道了数据库锁可以解决并发时数据安全的问题。但是在实际的开发中,如果我们要直接使用数据库锁来解决数据安全问题,那是非常麻烦的。数据库为了让我们更好的更方便的使用数据库锁,于是数据库为用户提供了自动锁机制,只要用户指定了会话的事务隔离级别,数据库就会分析事务中的SQL语句,然后自动为事务操作的数据资源上加上适合的锁。
事务隔离级别主要分为4个,在相同的数据环境下,使用相同的输入,执行相同的工作,设置不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力也是不同的。具体情况如下:
隔离级别 | 脏读 | 不可重复读 | 幻象读 | 第一类丢失更新 | 第二类丢失更新 |
---|---|---|---|---|---|
READ UNCOMMITED | 允许 | 允许 | 允许 | 不允许 | 充许 |
READ COMMITTED | 不允许 | 允许 | 允许 | 不允许 | 允许 |
REPEATABLE READ | 不允许 | 不允许 | 允许 | 不允许 | 不允许 |
SERIALIZABLE | 不允许 | 不允许 | 不允许 | 不允许 | 不允许 |
事务的隔离级别和数据库并发性是对立的,也就是说如果数据库的隔离级别最高,那么它的并发性也就越低,如果数据库的隔离级别越低,那么它的并发性就越高。
下面我们看一下JDBC对事务的支持。并不是所有的数据库都支持事务,即使支持事务的数据库也并非支持所有的事务隔离级别。
Commection默认情况下是自动提交的,也就是说每条执行的SQL都对应一个事务,为了能够将多条SQL当成一个事务执行,在Java中必须通过设置Connection中的setAutoCommit(false);方法阻止Connection自动提交,并可以通过Connection中的setTransactionIsolation()设置事务的隔离级别,Connection中定义了对应的上面介绍的4个数据库的隔离级别常量。并且可以通过调用Connection中的commit()方法提交事务,通过rollback()方法回滚事务。下面我们通过测试用例来演示在Java中怎么设置事务的隔离级别。
@Testpublic void test() { DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(); driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver"); driverManagerDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test"); driverManagerDataSource.setUsername("root"); driverManagerDataSource.setPassword("root"); Connection connection = null; try { connection = driverManagerDataSource.getConnection(); connection.setAutoCommit(false); // 关闭自动提交机制 connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); Statement statement = connection.createStatement(); int rows = statement.executeUpdate("INSERT INTO tbUser (strLoginId, strPassword, strName, dtCreateTime) VALUES ('test', 'test', 'test', NOW());"); rows = statement.executeUpdate("UPDATE tbUser SET strName = 'test2' WHERE lid = 2"); connection.commit(); } catch (SQLException e) { e.printStackTrace(); try { connection.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } }finally { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } }}
在JDBC2.0中,事务只能有两个操作,提交和回滚。但是,在我们日常开发时可能需要对事务进行更多的控制,而不是简单的提交和回滚。所以JDBC为了我们可以更加方便的控制,于是在JDBC3.0中引入了一个全新的保存点特性Savepoint。Savepoint接口允许用户将事务分隔为多个阶段,用户可以指定回滚到事务特定的保存点,而并不是向JDBC2.0中那样只能回滚到事务开始的起点。如下图所示:
下面的测试用例将演示如何在Java中使用保存点功能,在特定的问题时,回滚到指定的保存点,而非回滚整个事务。
@Testpublic void test() throws SQLException { DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(); driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver"); driverManagerDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test"); driverManagerDataSource.setUsername("root"); driverManagerDataSource.setPassword("root"); Connection connection = null; connection = driverManagerDataSource.getConnection(); connection.setAutoCommit(false); // 关闭自动提交机制 connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); Statement statement = connection.createStatement(); int rows = statement.executeUpdate("INSERT INTO tbUser (strLoginId, strPassword, strName, dtCreateTime) VALUES ('test', 'test', 'test', NOW());"); Savepoint savepoint = connection.setSavepoint("savePoint-1"); rows = statement.executeUpdate("UPDATE tbUser SET strName = 'test2' WHERE lid = 2"); connection.rollback(savepoint); connection.commit();}
按照上面的代码,在事务进行回滚时,只回滚到了保存点的位置,而保存点之前的操作,并不会回滚,然后在整个事务提交后,就相当于,只将INSERT语句执行并提交了,而UPDATE语句则进行了回滚。
备注:并非所有的数据库都支持保存点功能,用户可以通过DataBaseMetaData中的supportsSavepoints()方法来查看是否支持保存点功能。
本文由 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为:
2018/03/25 18:22