首先得知道程序是如何连接数据库的?
DataSource封装了Connection,Connection封装了Socket,程序最终是使用这个Socket来连接数据库的,对数据库的一些命令(execute、commit、rollback、close)都是通过Socket发送到数据库执行。而TransactionManager管理execute、commit、rollback、close方法。
数据库的事务原理
1、事务的概念
事务(Transaction)是访问或更新数据库中数据的一个程序执行单元(unit)。
特点:事务是恢复和并发控制的基本单位,具有四个特性(ACID):
- 原子性(Automicity):一个事务是一个不可分割的工作单位,一个事务中的操作要么都做,要么都不做。
- 一致性(Consistency):事务必须是使数据库从一个一致性状态变为另一个一致性状态。一致性和原子性密切相关。
- 隔离性(Isolation):一个事务的执行不能被其他事务干扰。即一个事务的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(Durability):一个事务一旦提交,它对数据库中数据的改变就是永久性的。接下来的其他操作或故障不应该对其有任何影响。
2、事务的基本流程
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,Spring是无法提供事务功能的。纯JDBC操作数据库事务的步骤:
1、获取连接:Connection conn = DriverManager.getConnection();
2、开启事务:conn.setAutoCommit(true/false);
3、执行CRUD
4、提交事务/回滚事务,conn.commit()/conn.rollback();
5、关闭连接:conn.close();
Spring的事务管理可以帮我们完成2和4,在使用@Transactional注解后,Spring会在有这个注解的地方开启事务的正常提交、异常回滚。
不配置事务的话,conn.setAutoCommit(true/false)默认为true,没有交给Spring来托管。而在交给Spring管理后,conn.setAutoCommit(true/false)则会被设置为false。
事务执行的流程:
<img src=""https://dddz97.oss-cn-hangzhou.aliyuncs.com/img/20200910194857.png"" style=""zoom: 40%;"" />
通过流程来看代码:
在DataSourceTransactionManager中,有doCommit()方法和doRollback()方法:
可以看到,两者都是调用了con的commit()和rollback()方法:
首先判断autoCommit是否为true,如果是true,就会抛出createSQLException异常。最后执行的是execSQL,即将""commit""这句sql提交到数据库执行。
可以看到rollback也是一样:
从代码中可以看到,commit、rollback操作,都是通过向数据库传输“commit”的sql语句、“rollback”的sql语句来进行的。所以真正的事务操作实际是在数据库里进行的(通过数据库的binlog或者redo log实现)。
3、事务在数据库中的过程
粗略的描述一下:
4、Spring事务的传播属性
Spring事务的传播属性,就是定义在多个事务同时存在的时候,Spring应该如何处理这些事务的行为。这些属性在TransactionDefinition中定义。
propagation | 说明 |
---|---|
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,则新建一个事务执行。这是Spring默认的事务传播属性。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。新建的事务将和被挂起的事务没有任何关系,是两个独立的事务。外部事务失败回滚之后,不能回滚内部事务执行的结果。内部事务失败抛出异常,被外部事务捕获,也可以不处理回滚操作。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果没有当前事务,则以非事务的方式执行 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,则抛出异常 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行操作,如果当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果有活动事务,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器有效 |
5、事务隔离级别
5.1 数据库中的事务隔离级别
隔离级别 | 值 | 导致的问题 |
---|---|---|
Read-Uncommitted(读未提交) | 0 | 导致脏读 |
Read-Committed(读已提交) | 1 | 避免脏读,允许不可重复读和幻读 |
Repeatable-Read(可重复读) | 2 | 避免脏读、不可重复读,允许幻读 |
Serializable(串行化) | 3 | 串行化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读,执行速度慢。 |
- 脏读:一个事务对数据进行了增、删、改,但未提交,另一个事务可以读取到未提交的数据。此时如果第一个事务回滚,那么第二个事务就读到了脏数据。
- 不可重复读:一个事务中发生了两次读操作,在第一次读和第二次读操作之间,另外一个事务对数据进行了修改,这时两次读取的数据是不一致的。
- 幻读:第一个事务对一定范围的数据进行了批量修改,第二个事务在这个范围内增加了一条数据,此时第一个事务就会丢失对新增数据的修改。
大部分数据库(SQLServer、Oracle、pgSQL)的事务隔离级别是Read-Committed(读已提交),少部分(MySQL、InnoDB)是Repeatable-Read(可重复读)。
5.2 Spring中的事务隔离级别
常量 | 解释 |
---|---|
ISOLATION_DEFAULT | 这是PlatformTransactionManager默认的事务隔离级别,使用的是数据库默认的事务隔离级别。另外4个与JDBC的事务隔离级别相对应。 |
ISOLATION_READ_UNCOMMITTED | 这是最低的事务隔离级别,它允许另外一个事务看到这个事务未提交的数据。会产生脏读、不可重复读、幻读。 |
ISOLATION_READ_COMMITTED | 保证一个事务修改的数据在commit之后才能被另一个事务读取。 |
ISOLATION_REPEATABLE_READ | 防止脏读、不可重复读,但是可能出现幻读。 |
ISOLATION_SERIALIZABLE | 这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。 |