Spring中的事务管理
配置注解
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
创建事务bean,配置数据源属性
<tx:annotation-driven transaction-manager="transactionManager" />
事务注解驱动
声明式事务
@Transactional public void hasTranInsertData() { Book book = new Book(); book.setIsbn("0002"); book.setBookName("java编程思想"); book.setPrice(79); this.bookMapper.insertSelective(book); int i = 1 / 0; }
public void notHasTranInsertData() { Book book = new Book(); book.setIsbn("0003"); book.setBookName("算法导论"); book.setPrice(109); this.bookMapper.insertSelective(book); int i = 1 / 0; }
以上可以看出上面的数据回滚了,而下面的保存到数据库了。
事务的传播
REQUIRED | 如果现有的事务正在进行,当前方法应该在这个事务中运行,否则它该启动新事务,并在自己的十五中运行。 |
REQUIRES_NEW | 当前方法必须启动新事务,并在自己的事务中运行,如果现有的事务正在进行,它应该挂起。 |
SUPPORTS | 如果现有事务正在进行,当前方法应该运行在该事务中,否则它没有必要运行在事务中。 |
NOT_SUPPORTED | 当前方法不应该运行在事务中,如果现有事务正在运行,它应该挂起。 |
MANDATORY | 当前方法必须运行于事务中,如果没有事务在进行中,将抛出一个异常。 |
NEVER | 当前方法不应该运行于事务中,如果现有事务在运行中,将抛出一个异常。 |
NESTED | 如果现有事务正在进行,当前方法应该运行在嵌套的事务中,否则它应该启动一个新事务并运行在自己的事务之中。 |
例如:
@Override @Transactional public void nestedBook() { Book book = new Book(); book.setIsbn("0004"); book.setBookName("分布式架构"); book.setPrice(89); this.bookMapper.insertSelective(book); for (int i = 0; i < 10; i ++) { try { this.testService1.nestedAccount(i + 2); } catch (Exception e) { e.printStackTrace(); } } }
@Override @Transactional(propagation=Propagation.NESTED) public void nestedAccount(int i) { System.out.println(i); Account account = new Account(); account.setUsername("user" + i); account.setBalance(new Random().nextInt(100)); this.accountMapper.insertSelective(account); if (7 == i) { int j = 1 / 0; } }
这里一个REQUIRED事务去调用NESTED事务,NESTED特性是在嵌套的事务里,如果发生异常他只会回滚他自己的事务,而不会影响调用他的事务。
他与REQUIRES_NEW的区别是,前面方法没有事务是每次需要创建自己的事务,如果前面有事务时,前面方法如果报错了会回滚所有操作,而REQUIRES_NEW不会回滚操作。
NESTED的好处在于既能保证前者方法抛异常时所有数据能回滚,也能保证后者方法一个抛异常不会影响后续的操作,只会回滚后者操作失败的数据。
隔离事务
DEFAULT | 使用底层数据库的默认隔离级别。对于大部分数据库,默认隔离级别是READ_COMMITED,mysql是REPEATABLE_READ |
READ_UNCOMMITED | 允许事务读取其他事务的未提交修改。可能发生脏读数据、不可重复读和幻读问题。 |
READ_COMMITED | 仅允许事务读取其他事务已经提交的修改。能够避免脏读数据问题,但是不可重复读和幻读问题仍然可能发生。 |
REPEATABLE_READ | 确保事务能够多次从一个字段读到相同值。在本事务期间,其他事务的更新被禁止。能够避免脏读数据和不可重复读问题,但是幻读问题仍然可能发生。 |
SERIALIZABLE | 确保一个事务能从表中多次读取相同的行。在事务期间,其他事务做出的对该表插入、更新和删除将被禁止。能避免所有并发性问题,但是性能将会很低。 |
RollBack属性
默认情况下,只有非受控异常(也就是RuntimeException和Error类型)将导致事务回滚,而受控异常不会。
@Override @Transactional public void hasTranInsertData() throws FileNotFoundException, ClassNotFoundException { Book book = new Book(); book.setIsbn("0005"); book.setBookName("java编程思想"); book.setPrice(79); this.bookMapper.insertSelective(book); try { int i = 1 / 0; } catch (Exception e) { throw new FileNotFoundException("错误"); } }
严重警告:上面的这段代码是不会回滚的,就因为抛出的异常时受控异常
rollbackFor:会发生回滚的异常
noRollbackFor:不会发生回滚的异常
将@Transaction变为下面这样
@Transactional(rollbackFor=FileNotFoundException.class, noRollbackFor=ClassNotFoundException.class)
改变之后就会进行回滚
如果抛出的异常改为
throw new ClassNotFoundException("错误");
则又不会回滚
超时和只读
timeout:表示事务在被强制回滚之前存活的时间。这能够避免长时间的事务占用资源。
readOnly:表示该事务仅仅读取而不更新数据。只读标志只是让资源优化事务的一个提醒,如果试图写入,资源不一定会发生故障。