一、事务的四大特性(ACID)
特性 | 解释 |
---|---|
原子性 ( Atomicity ) | 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。 例:同个事务有两个操作数据库的方法,如果第一个成功,第二个失败,则会自动回滚,导致两个操作都无效。 |
一致性 ( Consistency ) | 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。 例:用户两张卡账户有3000元,不管怎么转,账户总额都是3000. |
隔离性 ( Isolation ) | 隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。 例:两个并发的事务a1和a2。a1在执行的时候读取不到a2执行的数据,同理a2也是。 |
持久性 ( Durability ) | 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。 例:事务提交后,数据库即使断电,数据也不会丢失。 |
二、事务的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
级别 | 解释 |
---|---|
Read Uncommitted (读取未提交) | 最低的隔离级别,可以读取其他事务还未提交的数据,可能会导致脏读、幻读、不可重复读。 |
Read Committed (读取已提交) | 可以读取其他事务还已提交的数据,可能会导致幻读、不可重复读。(oracle默认的) |
Repeated Read (可重复读) | 对数据库中同一字段的多次读取结果都是一致的,除非被事务自身所修改,即不受其他事务的影响。可能会导致幻读。(mysql默认的) |
Serializable (可串行化) | 最高的隔离级别,即不允许并发执行事务。不会导致脏读、幻读、不可重复读。 |
级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 |
---|---|---|---|
Read Uncommitted | 是 | 是 | 是 |
Read Committed | 否 | 是 | 是 |
Repeated Read | 否 | 否 | 是 |
Serializable | 否 | 否 | 否 |
三、脏读、幻读、不可重复读
定义 | 解释 |
---|---|
脏读 | 事务a1处理过程里读取了另一个未提交的事务a2中的数据。 注:如果a2的数据发生回滚,则出现了脏数据。 |
幻读(虚读) | 事务a1查询两次发现返回的数据量不同。 注:两次查询间,有其他业务插入了新数据。 |
不可重复读 | 事务a1多次读取数据库的同一数据返回不同结果。 注:事务a1在读数据的同时,其他事务也在操作数据。 |
四、事务传播性行为
@Transactional(propagation=Propagation.XXX)
XXX | 解释 | 举例:假设有两个类,分别有方法A和B。其中B有@Transactional(propagation=Propagation.XXX) 注解,由A调用B的方法。 |
---|---|---|
NOT_SUPPORTED | 容器不为这个方法开启事务 注:在类上面加了 @Transactional ,如果想类中某个方法不开启事务,则设置这个即可。 | 1 如果A有事务,B抛出异常,A不捕获的话,A的事务被回滚,B出现异常前数据库操作不受影响。 2 如果A有事务,B抛出异常,A捕获的话,A的事务不受影响,B出现异常前的数据操作不受影响。 |
REQUIRED | 如果有事务, 那么加入事务, 没有的话新建一个(默认情况下) | 1 如果B抛出异常,A没有捕获,则A和B的操作都会回滚; 2 如果B抛出异常,A捕获异常并正常提交事务,抛出异常:Transaction rolled back because it has been marked as rollback-only。 3 如果A运行期间异常,则A和B的操作都会回滚; |
REQUIRES_NEW | 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务 | 1 如果A有事务,A抛出异常,A事务被回滚,但B.事务不受影响 2 如果A有事务,B抛出异常,A不捕获的话,A和B的事务都会被回滚。 3 如果A有事务,B抛出异常,A捕获的话,A事务不受影响,B事务回滚。 |
MANDATORY | 必须在一个已有的事务中执行,否则抛出异常 | 1 如果A没有事务,B抛出异常:No existingtransaction found for transaction marked with propagation ‘mandatory’。 2 如果A有事务,A抛出异常,则A和B的操作都会回滚; 3 如果A有事务,B抛出异常,A没有捕获,则A和B的操作都会回滚; 4 如果A有事务,B抛出异常,A捕获异常并正常提交事务,抛出Transaction rolled back because it has been marked as rollback-only的异常。 |
NEVER | 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反) | 1. 如果A有事务,则会出现异常Existingtransaction found for transaction marked with propagation ‘never’。。 |
SUPPORTS | 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务. | 1 如果A没有事务,则A和B的运行出现异常都不会回滚。 2 如果A有事务,A抛出异常,则A和B的操作都会回滚; 3 如果A有事务,B抛出异常,A没有捕获,则A和B的操作都会回滚; 4 如果A有事务,B抛出异常,A捕获异常并正常提交事务,抛出Transaction rolled back because it has been marked as rollback-only的异常。 |
NESTED | 如果当前存在事务,则在嵌套事务内执行。如果没有,则新建一个 | 1 如果A没有事务,则B开启新事务,B抛出异常,B回滚,A不回滚;A抛出异常,则A和B操作不回滚。 2 如果A有事务,A抛出异常,则A和B的操作都会回滚; 3 如果A有事务,B抛出异常,A没有捕获,则A和B的操作都会回滚; 4 如果A有事务,B抛出异常,A捕获异常并正常提交事务,抛出Transaction rolled back because it has been marked as rollback-only的异常。 |
五、Transactional常用属性
参数 | 解释 |
---|---|
readOnly | 设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。 例:@Transactional(readOnly=true) |
rollbackFor | 设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。 例: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
rollbackForClassName | 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。 例: 指定单一异常类名称:@Transactional(rollbackForClassName=“RuntimeException”) 指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,“Exception”}) |
noRollbackFor | 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。 例: 指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName | 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。 例: 指定单一异常类名称: @Transactional(noRollbackForClassName=“RuntimeException”) 指定多个异常类名称: @Transactional(noRollbackForClassName={“RuntimeException”,“Exception”}) |
propagation | 该属性用于设置事务的传播行为,第四大点 |
isolation | 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 |
timeout | 该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |
六、注意点
- 在同一事务 读取不到未提交的数据
解:我用的hibernate框架,在同一事务中,一边用框架封装的getsession新增数据,一边用jdbcTemplate查看数据。所以新增和查看对应的数据库连接不是同一个。然后我将查询数据也改为框架封装的getsession,成功读取到未提交的数据!