一、什么是事务?
事务是由步数据库操作序列组成的逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行
二、事务的特性(ACID)
1. 原子性
- 事务是一个不可分割的最小工作单位,事务中的操作要么都发生,要么都不发生
2. 一致性
- 事务必须是数据库从一个一致性的状态变换到另外一个一致性的状态
3. 持久性
- 事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
4. 隔离性
- 一个事务的执行不能被其他事务所干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事物之间互相不能干扰
三、数据库的并发问题:
1. 更新丢失
- 两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。这是因为系统没有执行任何的锁操作,因此并发并没有被隔离开来。
2. 脏读
- 对于两个事务T1,T2,T1读取了已经被T2更新但还没有提交的字段。之后,若T2回滚,T1读取的内容就是临时且无效的。
3. 不可重复读
- 对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段。之后,T1再次读取同一个字段,值就不一样了。
4. 幻读
- 对于两个事务T1,T2,T1从一个表中读取了字段,然后T2在该表中插入了一些新的行。之后,如果T1再次读取同一个表,就会多出几行。
四、事务的四种隔离级别
为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
1. 读未提交(Read Uncommitted) 只处理更新丢失。如果一个事务已经开始写数据,则不允许其他事务同时进行写操作,但允许其他事务读此行数据。可通过“排他写锁”实现。
2. 读提交(Read Committed) 处理更新丢失、脏读。读取数据的事务允许其他事务继续访问改行数据,但是未提交的写事务将会禁止其他事务访问改行。可通过“瞬间共享读锁”和“排他写锁”实现。(一般情况下,使用此级别即可)
3. 可重复读取(Repeatable Read) 处理更新丢失、脏读和不可重复读取。读取数据的事务将会禁止写事务,但允许读事务,写事务则禁止任何其他事务。可通过“共享读锁”和“排他写锁”实现。
4. 序列化(Serializable) 提供严格的事务隔离。要求失去序列化执行,事务只能一个接一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
五、实现机制
六、Spring事务管理
一般地,用户的每次请求都对应一个业务逻辑方法,而一个业务逻辑方法往往包括一系列数据库原子访问操作,并且这些数据库原子访问操作应该绑定成一个事务来执行。然而,在使用传统的事务编程策略时,程序代码必然和具体的事务操作代码耦合,而使用Spring事务管理策略恰好可以避免这种尴尬。Spring的事务管理提供了两种方式:编程式事务管理和声明式事务管理。
1. 声明式事务 Spring 的声明式事务管理是建立在 Spring AOP 机制之上的,其本质是对目标方法前后进行拦截,并在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中作相关的事务规则声明(或通过等价的基于标注的方式),便可以将事务规则应用到业务逻辑中。总的来说,声明式事务得益于 Spring IoC容器 和 Spring AOP 机制的支持:IoC容器为声明式事务管理提供了基础设施,使得 Bean 对于 Spring 框架而言是可管理的;而由于事务管理本身就是一个典型的横切逻辑(正是 AOP 的用武之地),因此 Spring AOP 机制是声明式事务管理的直接实现者。声明式事务又分为两种方式:通过XML配置声明某方法的事务特征;通过注解声明某方法的事务特征
基于 @Transactional 的声明式事务管理
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
public Object save1(){
// 新增用户
User user = new User();
user.setUsername("alpha");
user.setSalt(CommunityUtil.generateUUID().substring(0,5));
user.setPassword(CommunityUtil.md5("123" + user.getSalt()));
user.setEmail("alpha@qq.com");
user.setHeaderUrl("http://image.nowcoder.com/head/99t.png");
user.setCreateTime(new Date());
userMapper.insertUser(user);
Integer.valueOf("abc");// 异常
return "ok";
}
常用的事务管理的机制: REQUIRED:支持当前事务(外部事务),如果不存在则创建新事务 REQUIRES_NEW:创建一个新事务,并且暂停当前事务 NESTED:如果当前存在事务(外部事务),则嵌套在该事务中执行(独立的提交和回滚),否则就会和REQUIRED一样
2. 编程式事务 通过TransactionTemplate管理事务,并通过它执行数据库的操作
@Autowired
private TransactionTemplate transactionTemplate;
public Object save2(){
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 调用回调方法
return transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
// 新增用户
User user = new User();
user.setUsername("beta");
user.setSalt(CommunityUtil.generateUUID().substring(0,5));
user.setPassword(CommunityUtil.md5("123" + user.getSalt()));
user.setEmail("beta@qq.com");
user.setHeaderUrl("http://image.nowcoder.com/head/999t.png");
user.setCreateTime(new Date());
userMapper.insertUser(user);
Integer.valueOf("abc");// 异常
return "ok";
}
});
}
通常情况下,我们有限选择声明式事务,较为简单方便。
当我们的业务逻辑比较复杂,或只需对其中一部分的逻辑进行事务的管理,选择编程式事务进行管理。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/2169.html