这几天特意去总结了一下spring事务这一块,之前只是有一个模糊的概念
百度一波,就有很多总结。我借鉴了很多前辈的总结经验
异常的一些基本知识
- 异常的架构
异常的继承结构:Throwable为基类,Error和Exception继承Throwable。Error和RuntimeException及其子类成为未检查异常(unchecked),其它异常成为已检查异常(checked)。
- Error异常
Error表示程序在运行期间出现了十分严重、不可恢复的错误,在这种情况下应用程序只能中止运行,例如JAVA 虚拟机出现错误。Error是一种unchecked Exception,编译器不会检查Error是否被处理,在程序中不用捕获Error类型的异常。一般情况下,在程序中也不应该抛出Error类型的异常。
- RuntimeException异常
Exception异常包括RuntimeException异常和其他非RuntimeException的异常。
RuntimeException 是一种Unchecked Exception,即表示编译器不会检查程序是否对RuntimeException作了处理,在程序中不必捕获RuntimException类型的异常,也不必在方法体声明抛出 RuntimeException类。RuntimeException发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去捕获RuntimeException。
- Checked Exception异常
Checked Exception异常,这也是在编程中使用最多的Exception,所有继承自Exception并且不是RuntimeException的异常都是checked Exception,上图中的IOException和ClassNotFoundException。JAVA 语言规定必须对checked Exception作处理,编译器会对此作检查,要么在方法体中声明抛出checked Exception,要么使用catch语句捕获checked Exception进行处理,不然不能通过编译。
1.事务有四个特性:ACID
- 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
- 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
- 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
- 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。
2 核心接口
Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略。
Spring事务管理涉及的接口的联系如下:
3.传播行为
事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:
传播行为 | 含义 |
---|---|
PROPAGATION_REQUIRED | 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务 |
PROPAGATION_SUPPORTS | 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行 |
PROPAGATION_MANDATORY | 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常 |
PROPAGATION_REQUIRED_NEW | 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_NOT_SUPPORTED | 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_NEVER | 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常 |
PROPAGATION_NESTED | 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务 |
4.使用
事务使用方式有2种,编程式和声明式(注解)
1.注解通过@Transactional 在方法上,一般使用默认的传播方式就可以,当前没有事务,就新启一个事务;当前有事务,就加入到当前事务种。
2.声明式:
1)定义事务
2)通过commit/rollback 来实现事务执行/回滚
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try{
txManager.commit(status);
}catch(Exception ex){
txManager.rollback(status);
}
5.事务传播机制
个人经验简单描述一下,采用声明式事务@Transactional
基本情景:2个类A,B,都是一个各自表插入一条数据saveA()和saveB(),在saveA()实现中调用saveB(),并在saveB()中抛出runTimeException异常
1.如果2个都不设置事务,插入语句
结果,a和b都成功插入一条数据
2.在saveA()设置事务
结果:a和b都失败
3.在saveB()设置事务
结果:a插入成功,b失败
4.在saveA(),saveB()都设置事务
结果:a和b都失败
以上都是默认传播行为
总结:
其实这些看了差不多都能明白,但是还是自己动手才会有更深的体会
最后附上我写的代码
1.2个对象
User
@Data public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String password; private int age; private String remark; }
Friend
@Data public class Friend implements Serializable { private Long id; private Long userId; private String name; private int age; private String remark; }
没有get,set方法是使用idea 的lombox插件,自动补全
@GeneratedValue(strategy = GenerationType.IDENTITY)是实现插入能够直接返回id值
service层
@Override @Transactional public int saveUser(User user) { int flag = userDao.save(user); Friend friend = new Friend(); friend.setAge(20); friend.setName(user.getName()+"--"); friend.setUserId(user.getId()); friendService.saveFriend(friend); return flag; }
@Override public int saveFriend(Friend friend) { int flag = friendDao.save(friend); throw new RuntimeException("test"); // return flag; }
只要的代码就这一块,很简单。