Bootstrap

事务,事务的特点,事务并发带来的问题,实现事务管理

1.什么是事务

1、事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性

2、事务是一系列动作,它们被当作一个独立的工作单元,这些动作要么全部完成,要么全不起作用。

2.事务的特点  ACID

1.原子性:事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成 要么完全不起作用

2.一致性:一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也 就是说一个事务执行之前和执行之后都必须处于一致性状态。举例来说,假设用户A和用户B两者的钱加起 来一共是1000,那么不管A和B之间如何转账、转几次账,事务结束后两个用户的钱相加起来应该还得是 1000,这就是事务的一致性。

3.隔离性:可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开 来, 防止数据损坏

4.持久性: 一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中。事务完成--后面在发生任何错误都不应该受影响

 3.事务并发带来的问题

3.1脏读

对于两个事务T1 和T2 , T1 读取了已经被T2 更新但还没有被提交的字段。之后,若T2 进行回滚,T1读取的内容就是临时且无效的

例如:某一事务A读取到了事务B未提交的数据。

一个事务读取到另一个事务未提交的数据。如: 张三说还李四一万块,开启事务之后就将钱转给李四,然后 打电话叫李四看钱到没,李四查看说到了,然后张三直接回滚事务(rollback),张三自己账户的钱没扣, 却说已经还给李四了,李四再查看,发现自己的账户没有转入的钱,只能当冤大头了

事务A先查询张三年龄,随后事务B修改张三年龄,然后事务A又读取张三年龄,然后事务 A提交事务,事务B回滚,那事务A第二次查出的数据就是错的,所以是脏读。

3.2不可重复读

在一个事务内,多次读取同一个数据,却返回了不同的结果。实际上,这是因为在该事务 间隔读取数据的期间,有其他事务对这段数据进行了修改,并且已经提交,就会发生不可重复读事故。

在同一个事务下,多次读取同一个记录但返回的数据不一样(这个是针对:更新操作)。如: A,B两个事务 同时访问数据库,B事务查询数据库发现A账户还有一万块钱,此时A事务购买了东西,账户被扣了五千元,B 忘记了账户还有多少钱,再查看,发现卡上只有五千元了,这种情况就是不可重复读。

3.3幻读

幻读是指当事务不独立执行时,插入或者删除另一个事务当前影响的数据而发生的一种类似幻觉的现象。

在一个事务中两次读取的条数不一样。

4.使用事务的隔离级别来解决

1.READ UNCOMMITTED读未提交;


2.READ COMMITTED读已提交:一个事务只允许读取另一个事务提交的数据。


 3.repeatable read(可重读):这是MySQL的默认事务隔离级别,同一事务的多个实例在并发读取数据时,会看到同样的数据。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。可解决脏读、不可重复读


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

1.查询全局隔离级别: select @@global.transaction_isolation

2.查看当前会话隔离级别 select @@transaction_isolation;

3. 设置隔离级别SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED

5.实现事务管理

5.1jdbc完成事务管理

public class Test02 {
    public static void main(String[] args) throws SQLException {
        Connection conn = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day06?serverTimezone=Asia/Shanghai", "root", "123456");
            conn.setAutoCommit(false);//改为手动提交
            PreparedStatement ps=conn.prepareStatement("update tbl_user set money=money-1000 where username='gg'");
            ps.executeQuery();
            //制造异常
            int c=10/0;
            ps= conn.prepareStatement("update tbl_user set money=money+1000 where username='zz'");
            ps.executeQuery();
            System.out.println("转账成功");
            conn.commit();//事务提交
        } catch (Exception e) {
            System.out.println("转账失败");
            /*应该把上面这些动作放在一个事务中管理。但是jdbc默认事务是自动提交。*/
            conn.rollback();
        }
    }
}

5.2spring完成事务管理        底层采用的就是aop

1. 引入事务管理的依赖--spring-tx

<!--    事务管理依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>

 2.在spring.xml里面配置事务切面类

<!--    注册事务管理:id必须为transactionManager-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="ds"/>
    </bean>
<!--    开启事务注解驱动-->
    <tx:annotation-driven/>

 3.在业务层使用事务注解

@Transactional

业务层有多个动作组成。必须使用事务管理

 

;