@Transactional详解
@Transactional 是声明式事务管理 编程中使⽤的注解
1 .添加位置
1)接⼝实现类或接⼝实现⽅法上,⽽不是接⼝类中。
2)访问权限:public 的⽅法才起作⽤。@Transactional 注解应该只被应⽤到 public ⽅法上,这是由 Spring AOP 的本质决定的。系统设计:将标签放置在需要进⾏事务管理的⽅法上,⽽不是放在所有接⼝实现类上:只读的接⼝就不需要事务管理,由于配置了
@Transactional就需要AOP拦截及事务的处理,可能影响系统性能。
3)错误使⽤:
1.接⼝中A、B两个⽅法,A⽆@Transactional标签,B有,上层通过A间接调⽤B,此时事务不⽣效。
2.接⼝中异常(运⾏时异常)被捕获⽽没有被抛出。
默认配置下,spring 只有在抛出的异常为运⾏时 unchecked 异常时才回滚该事务,
spring aop应用场景
也就是抛出的异常为RuntimeException 的⼦类(Errors也会导致事务回滚),
⽽抛出 checked 异常则不会导致事务回滚。可通过 @Transactional rollbackFor进⾏配置。
3.多线程下事务管理因为线程不属于 spring 托管,故线程不能够默认使⽤ spring 的事务,
也不能获取spring 注⼊的 bean 。
在被 spring 声明式事务管理的⽅法内开启多线程,多线程内的⽅法不被事务控制。
⼀个使⽤了@Transactional 的⽅法,如果⽅法内包含多线程的使⽤,⽅法内部出现异常,
不会回滚线程中调⽤⽅法的事务。
2.声明式事务管理实现⽅式:
基于 tx 和 aop 名字空间的 xml 配置⽂件
// 基本配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance" xmlns:context="/schema/context"
xmlns:mvc="/schema/mvc" xmlns:aop="/schema/aop"
xmlns:task="/schema/task" xmlns:jms="/schema/jms"
xmlns:tx="/schema/tx"
xsi:schemaLocation="/schema/mvc /schema/mvc/spring-mvc-4.1.xsd
/schema/beans /schema/beans/spring-beans-4.1.xsd
/schema/context /schema/context/spring-context-4.1.xsd
/schema/aop /schema/aop/spring-aop-4.1.xsd
/schema/task /schema/task/spring-task-4.1.xsd
/schema/tx /schema/tx/spring-tx-4.1.xsd
/schema/jms /schema/jms/spring-jms-4.1.xsd">
<bean name="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="shardingDataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
// MyBatis ⾃动参与到 spring 事务管理中,⽆需额外配置,
只要 batis.spring.SqlSessionFactoryBean 引⽤的数据源与
DataSourceTransactionManager 引⽤的数据源⼀致即可,否则事务管理会不起作⽤。
// <annotation-driven> 标签的声明,
是在 Spring 内部启⽤ @Transactional 来进⾏事务管理,使⽤ @Transactional 前需要配置。
3. @Transactional注解
@Transactional 实质是使⽤了 JDBC 的事务来进⾏事务控制的
@Transactional 基于 Spring 的动态代理的机制
@Transactional 实现原理:
1) 事务开始时,通过AOP机制,⽣成⼀个代理connection对象,
并将其放⼊ DataSource 实例的某个与 DataSourceTransactionManager 相关的某处容器中。
在接下来的整个事务中,客户代码都应该使⽤该 connection 连接数据库,
执⾏所有数据库命令。
[不使⽤该 connection 连接数据库执⾏的数据库命令,在本事务回滚的时候得不到回滚]
(物理连接 connection 逻辑上新建⼀个会话session;
DataSource 与 TransactionManager 配置相同的数据源)
2) 事务结束时,回滚在第1步骤中得到的代理 connection 对象上执⾏的数据库命令,
然后关闭该代理 connection 对象。
(事务结束后,回滚操作不会对已执⾏完毕的SQL操作命令起作⽤)
4.声明式事务的管理实现本质:
事务的两种开启⽅式:
显⽰开启 start transaction | begin,通过 commit | rollback 结束事务
关闭数据库中⾃动提交 autocommit set autocommit = 0;MySQL 默认开启⾃动提交;通过⼿动提交或执⾏回滚操作来结束事务Spring 关闭数据库中⾃动提交:在⽅法执⾏前关闭⾃动提交,⽅法执⾏完毕后再开启⾃动提交
// org.springframework.jdbc.datasource.DataSourceTransactionManager.java 源码实现
// switch to manual commit if necessary. this is very expensive in some jdbc drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (autocommit()) {
txobject.setmustrestoreautocommit(true);
if (logger.isdebugenabled()) {
logger.debug("switching jdbc connection [" + con + "] to manual commit");
}
con.setautocommit(false);
}
问题:
关闭⾃动提交后,若事务⼀直未完成,即未⼿动执⾏ commit 或 rollback 时如何处理已经执⾏过的SQL操作?
C3P0 默认的策略是回滚任何未提交的事务
C3P0 是⼀个开源的JDBC连接池,它实现了数据源和 JNDI 绑定,⽀持 JDBC3 规范和 JDBC2 的标准扩展。⽬前使⽤它的开源项⽬有Hibernate,Spring等
JNDI(Java Naming and Directory Interface,Java命名和⽬录接⼝)是SUN公司提供的⼀种标准的Java命名系统接⼝,JNDI提供统⼀的客户端API,通过不同的访问提供者接⼝JNDI服务供应接⼝(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和⽬录系统,使得Java 应⽤程序可以和这些命名服务和⽬录服务之间进⾏交互
-------------------------------------------------------------------------------------------------------------------------------
5. spring 事务特性
spring 所有的事务管理策略类都继承⾃ ansaction.PlatformTransactionManager 接⼝
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
事务的隔离级别:是指若⼲个并发的事务之间的隔离程度
1. @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读,
不可重复读) 基本不使⽤
2. @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)
3. @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)
4. @Transactional(isolation = Isolation.SERIALIZABLE):串⾏化
事务传播⾏为:如果在开始当前事务之前,⼀个事务上下⽂已经存在,此时有若⼲选项可以指定⼀个事务性⽅法的执⾏⾏为
1. TransactionDefinition.PROPAGATION_REQUIRED:
如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。这是默认值。
2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:
创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。
3. TransactionDefinition.PROPAGATION_SUPPORTS:
如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
5. TransactionDefinition.PROPAGATION_NEVER:
以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
6. TransactionDefinition.PROPAGATION_MANDATORY:
如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。
7. TransactionDefinition.PROPAGATION_NESTED:
如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;
如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
上表字段说明:
1. value :主要⽤来指定不同的事务管理器;
主要⽤来满⾜在同⼀个系统中,存在不同的事务管理器。
⽐如在Spring中,声明了两种事务管理器txManager1, txManager2.然后,
⽤户可以根据这个参数来根据需要指定特定的txManager.
2. value 适⽤场景:在⼀个系统中,需要访问多个数据源或者多个数据库,
则必然会配置多个事务管理器的
3. REQUIRED_NEW:内部的事务独⽴运⾏,在各⾃的作⽤域中,可以独⽴的回滚或者提交;
⽽外部的事务将不受内部事务的回滚状态影响。
4. ESTED 的事务,基于单⼀的事务来管理,提供了多个保存点。
这种多个保存点的机制允许内部事务的变更触发外部事务的回滚。
⽽外部事务在混滚之后,仍能继续进⾏事务处理,即使部分操作已经被混滚。
由于这个设置基于 JDBC 的保存点,所以只能⼯作在 JDB C的机制。
5. rollbackFor:让受检查异常回滚;即让本来不应该回滚的进⾏回滚操作。
6. noRollbackFor:忽略⾮检查异常;即让本来应该回滚的不进⾏回滚操作。
6.其他:
1. 事务⽅法的嵌套调⽤会产⽣事务传播。
2. spring 的事务管理是线程安全的
3. ⽗类的声明的 @Transactional 会对⼦类的所有⽅法进⾏事务增强;
⼦类覆盖重写⽗类⽅式可覆盖其 @Transactional 中的声明配置。
4. 类名上⽅使⽤ @Transactional ,类中⽅法可通过属性配置来覆盖类上的 @Transactional 配置;  ⽐如:类上配置全局是可读写,可在某个⽅法上改为只读。