说起事务,大家应该多多少少用过,尤其是在一个 service 方法中调用多次 dao 操作,我们一定要用到事务 (
@Transational注解
),那么这个事务的默认隔离级别和传播机制是什么呢?
先来讲讲 脏读
不可重复读
和 幻读
。
脏读: 我们在并发编程中是很熟悉的,通俗的讲就是你读得数据已经被修改了,已经过时失去意义了。
不可重复读: 同一个事务里面多次读取同一行数据,却返回不同的结果。
幻读:同样一笔查询在整个事务过程中多次执行后,查询所得的结果集不一样。
事务四大特性 ACID
1. 原子性 (Atomicity)
要求事务所包含的全部操作是一个不可分割的整体,如果有一步发生异常,则全部不提交。
2. 一致性 (Consistency)
A 给 B 转钱,A 减和 B 增这两个操作必须保持一致。
3. 隔离性 (Isolation)
事务会将一部分数据与其他事务隔离,防止脏读等。
4. 持久性 (Durability)
事务的结果被写到持久化存储器中。
事务四大隔离级别
隔离级别越高,则性能相对越低,反之亦然。
1. Read Uncommitted
最低的隔离级别,跟你直译的意思一样:可以读取其它事务未完成的结果。(脏读)
很明显, 脏读
不可重复读
和 幻读
这三个问题它都有。
2. Read Committed
大部分数据库采用的默认隔离级别,比上一个隔离级别多了限定:在该事务完成后,才能读取该事务的数据更新后的结果。
它可以避免脏读,但是也有不可重复读取和幻读的问题。
3. Repeatable Read
可以保证在整个事务的过程中,对同一笔数据的读取结果是相同的,不管其他事务是否同时在对同一笔数据进行更新,也不管其他事务对同一笔数 据的更新提交与否。
Repeatable Read 隔离级别避免了脏读和不可重复读取的问题,但无法避免幻读。
4. Serializable
最为严格的隔离级别,所有的事务操作都必须依次顺序执行,可以避免其他隔离级别遇到的所有问题,是最为安全的隔离级别, 但同时也是性能最差的隔离级别。
通常情况下,我们会使用其他隔离级别加上相应的并发锁的机制来控制对数据的访问,这样既保证 了系统性能不会损失太大,也能够一定程度上保证数据的一致性。
Spring 事务传播机制
从 JDBC 的事务说起
我们都知道,JDBC 给我们提供了事务。
1. `try{` 2. `con.setAutoCommit(false);//开启事务` 3. `......` 4. `con.commit();//try的最后提交事务` 5. `} catch() {` 6. `con.rollback();//回滚事务` 7. `}`
获取事务隔离级别
1. `Connection.getTransactionIsolation()`
设置事务隔离级别
1. `con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);`
Spring 事务机制
Spring 并不会直接管理事务,而是提供了事务管理器,将事务管理的职责委托给 JPA JDBC JTA DataSourceTransaction JMSTransactionManager 等框架提供的事务来实现。
那么,Spring 提供的事务管理器是什么呢?
是 PlatformTransactionManager.java
接口:
PlatformTransactionManager.java
Spring 提供的事务管理器。不同的事务遵循该事务管理器的 API,便能很轻松的交给 Spring 管理。
1. `public interface PlatformTransactionManager {` 2. `// 通过Transation定义 获取Transation` 3. `TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;` 4. `// 提交事务` 5. `void commit(TransactionStatus var1) throws TransactionException;` 6. `// 回滚事务` 7. `void rollback(TransactionStatus var1) throws TransactionException;` 8. `}`
可以看到它里面引用到了 TransactionDefinition
和 TransactionStatus
.
TransactionDefinition.java
它里面包含了事务的定义。
1. `public interface TransactionDefinition {` 2. `// 传播机制` 3. `int PROPAGATION_REQUIRED = 0;` 4. `int PROPAGATION_SUPPORTS = 1;` 5. `int PROPAGATION_MANDATORY = 2;` 6. `int PROPAGATION_REQUIRES_NEW = 3;` 7. `int PROPAGATION_NOT_SUPPORTED = 4;` 8. `int PROPAGATION_NEVER = 5;` 9. `int PROPAGATION_NESTED = 6;` 10. `// 隔离级别` 11. `int ISOLATION_DEFAULT = -1;` 12. `int ISOLATION_READ_UNCOMMITTED = 1;` 13. `int ISOLATION_READ_COMMITTED = 2;` 14. `int ISOLATION_REPEATABLE_READ = 4;` 15. `int ISOLATION_SERIALIZABLE = 8;` 16. `int TIMEOUT_DEFAULT = -1;` 18. `int getPropagationBehavior();` 19. `// 获取隔离级别` 20. `int getIsolationLevel();` 22. `int getTimeout();` 24. `boolean isReadOnly();` 26. `@Nullable` 27. `String getName();` 28. `}`
TransactionStatus.java
事务的状态。
1. `public interface TransactionStatus extends SavepointManager, Flushable {` 2. `boolean isNewTransaction();` 4. `boolean hasSavepoint();` 6. `void setRollbackOnly();` 8. `boolean isRollbackOnly();` 10. `void flush();` 12. `boolean isCompleted();` 13. `}`
Spring 默认事务使用
1. 代码方式使用
1. `@Autowired` 2. `private PlatformTransactionManager transactionManager;` 3. `public void testTX(){` 4. `DefaultTransactionDefinition definition = new DefaultTransactionDefinition();` 5. `TransactionStatus status = transactionManager.getTransaction(definition);` 6. `try {` 7. `// 业务逻辑` 8. `// ...` 10. `// 提交事务` 11. `transactionManager.commit(status);` 12. `}catch (Exception e){` 13. `// 发生异常,事务回滚` 14. `transactionManager.rollback(status);` 15. `}` 16. `}`
2. 注解方式使用
1. `@Transactional` 2. `void testTX2(){` 3. `// 业务逻辑 ...` 4. `}`
这不是玄学,它的底层是依靠 AOP 动态代理实现,其实重新渲染出的代码和第一个使用方式类似,不过大大减少了开发复杂度。
扩展:@Transactional 注解
1. `@Target({ElementType.METHOD, ElementType.TYPE})` 2. `@Retention(RetentionPolicy.RUNTIME)` 3. `@Inherited` 4. `@Documented` 5. `public @interface Transactional {` 7. `//指定使用的事务管理器` 8. `@AliasFor("transactionManager")` 9. `String value() default "";` 11. `@AliasFor("value")` 12. `String transactionManager() default "";` 13. `// 可选的事务传播行为设置` 14. `Propagation propagation() default Propagation.REQUIRED;` 15. `// 可选的事务隔离级别设置` 16. `Isolation isolation() default Isolation.DEFAULT;` 17. `// 事务超时时间设置` 18. `int timeout() default -1;` 19. `// 读写或只读事务,默认读写` 20. `boolean readOnly() default false;` 21. `// 导致事务回滚的异常类数组` 22. `Class extends Throwable>[] rollbackFor() default {};` 23. `// 导致事务回滚的异常类名字数组` 24. `String[] rollbackForClassName() default {};` 25. `// 不会导致事务回滚的异常类数组` 26. `Class extends Throwable>[] noRollbackFor() default {};` 27. `// 不会导致事务回滚的异常类名字数组` 28. `String[] noRollbackForClassName() default {};` 29. `}`
Spring 事务实践
非入门选手下面的 demo 可能会引起你的不适 (浪费时间)。 假设我要完成一个功能,当删除用户的时候,将与该用户有关的所有数据行都删除。
1. `public void delUser(Integer userId) {` 2. `// 删除和用户相关的信息` 3. `otherRepository.deleteByUserId(userId);` 4. `// 删除用户` 5. `userRepository.deleteById(userId);` 6. `}`
这样的写法一般来讲,会成功的完成任务。但是如果这样一段代码:
1. `public void delUser(Integer userId) {` 2. `// 删除和用户相关的信息` 3. `otherRepository.deleteByUserId();` 4. `if (true) {` 5. `throw new RuntimeException("xxx");` 6. `}` 7. `// 删除用户` 8. `userRepository.deleteById(userId);` 9. `}`
结果会是: deleteByUserId()
执行成功, deleteById()
执行失败,不满足数据的一致性。
所以我们需要事务来限制:要么全部执行,要么全部不执行 (方法中有异常就自动回滚)。那怎么实现呢,只需要在方法上加一个注解: @Transactional
1. `@Transactional` 2. `public void delUser(Integer userId) {` 3. `// 删除和用户相关的信息` 4. `otherRepository.deleteByUserId();` 5. `if (true) {` 6. `throw new RuntimeException("xxx");` 7. `}` 8. `// 删除用户` 9. `userRepository.deleteById(userId);` 10. `}`
Spring 加载第三方事务管理
比如我有个需求 (接着上次的强票系统 II),要求信息不能丢失,要用到 RabbitMQ 的事务管理,那怎么去加载到 Spring 的事务管理器中呢?
1. `@Bean` 2. `public ConnectionFactory connectionFactory() {` 3. `CachingConnectionFactory connectionFactory = new CachingConnectionFactory();` 4. `return connectionFactory;` 5. `}` 7. `@Bean` 8. `public RabbitTransactionManager rabbitTransactionManager(ConnectionFactory connectionFactory) {` 9. `return new RabbitTransactionManager(connectionFactory);` 10. `}`
我们只需要这样做便可以使的使用 @Transactional注解
来实现对 RabbitMQ 的事务管理, 其它框架也类似。