首页 > 技术文章 > 事务

wanghuaying 2018-08-20 15:18 原文

事务的定义:一组要么同时成功要么同时失败

1.数据事物讲解

1.1事务开始于

  • 连接到数据库上,并执行一条DML语句insert、update或delete

  • 前一个事务结束后,又输入了另一条DML语句

 

1.2事务结束

  • 执行commit或rollback语句。

  • 执行一条DDL语句,例如create table语句,在这种情况下,会自动执行commit语句。

  • 执行一条DDL语句,例如grant语句,在这种情况下,会自动执行commit。

  • 断开与数据库的连接

  • 执行了一条DML语句,该语句却失败了,在这种情况中,会为这个无效的DML语句执行rollback语句

2.事务的四大特点

  • actomicity(原子性)

    表示一个事务内的所有操作是一个整体,要么全部成功,要么全部失败

  • consistency(一致性)

    表示一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前状态

  • isolation(隔离性)

    事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会   查看中间状态的数据。

事务之间相互隔离

  • durability(持久性)

    持久性事务完成之后,它对于系统的影响是永久性的。

3.事务的隔离的级别

SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

Read Committed(读取提交内容)

​ 这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

不可重复读:同一个事务下,两条查询语句结果不一致(读取到了另一个事务修改后的数据)

Repeatable Read可重读

​ 这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。

幻读:同一个事务下,两条查询语句结果条数不一致(读取到了另一个事务新增后的数据)

Serializable 可串行化​ 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

​ 这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。

例如:

​   脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

​   不可重复读(Non-repeatableread):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

​   幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的

================mysql中===============

默认隔离级别是:Repeatable Read可重读

查看当前的事务隔离级别:select  @@tx_isolation;

更改当前的事务的隔离级别:set transaction isolation level 四个级别之一。

设置隔离级别必须在事务之前

read uncommitted:不能避免脏读,不可重复读,幻读(虚读)

read  committed :避免脏读,不可避免不可重复,幻读

repeatable read:避免脏读,不可重复读,不能避免幻读

Serializable:都能避免

4.Java中事务应用

如果jdbc连接处于自动提交模式,默认情况下,则每个sql语句在完成后都会提交到数据库

事务使你能够控制是否何时更改应用于数据库。它将单个SQL语句或一组SQL语句视为一个简单的单元,

如有任何语句失败,则整个事务将失败。

要启动手动事务支持,而不是JDBC驱动程序默认使用的自动提交模式,请使用connection对象的

setAutoCommit()方法。如果将boolean false 传递给setAutoCommit(),则关闭提交,

我们可以传递一个布尔值true来重新打开它。

4.1事务的提交和回滚

完成更改后,我们要提交更改,然后在连接对象上调用commit()方法,如:conn.commit();

否则,要使用连接名为coon的数据库回滚更新,请使用以下代码:conn.rollback();

4.2使用Savepoints

新的jdbc 3.0 Savepoint接口为你提供了额外的事务控制。

设置保存点时,可以在事务中定义逻辑回滚点。如果通过保存点发生错误,则可以使用回滚的方式来撤销

所有更改或仅保存在保存点之后所做的更改。

connection对象有两种新的方法来帮助你管理保存点

setSavepoint(String savepointName):定义新的保存点。它还返回一个是savepoint对象。

releaseSavepoint(Savepoint savepointName):删除保存点。请注意,它需要一个savepoint

对象作为参数。此对象通常是由setSavepoint ()方法生成保存点。

1、要取消掉JDBC的自动提交:void setAutoCommit(boolean autoCommit)

2、执行各个SQL语句,加入到批处理之中

3、如果所有语句执行成功,则提交事务 commit();如果出现了错误,则回滚:rollback()


  ***************************   * 转账业务来理解事务*  *******************************

数据库

 

MySQL中开两个线程来查看银行账户是否进行转账()注意:每次在执行的时候都要刷新一下

 

        (线程一)

       (线程二)

Java中事务表达方式的理解

public class TestTransaction {
    @Test
    public void testTransaction() {
        Connection connection  = DBUtils.getConnection();
        PreparedStatement ps = null;
        //控制事务
        try {
            connection.setAutoCommit(false);//不自动提交事务  等价于数据库中执行:begin;start transaction;
            
            
            String sql = "update account1 set money = money -100 where name=?";
            
            ps  = connection.prepareStatement(sql);
            ps.setString(1, "k");
            
            //执行操作
            ps.executeUpdate();
            //ps.addBatch();
            
            //模拟断电
            int i = 10/0;
            
            String sql1 = "update account1 set money = money +100 where name=?";
            ps  = connection.prepareStatement(sql1);
            ps.setString(1, "h");
            ps.executeUpdate();
            
            //提交事务
            connection.commit();
            
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }catch (Exception e) {
            //回滚 到事务之前的状态
            try {
                connection.rollback();
            } catch (SQLException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }finally {
            //提交事务
            try {
                connection.commit();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

 


 

推荐阅读