事务概述
在JavaEE企业级开发的应用领域,为了保证数据的完整性和一致性,必须引入数据库事务的概念,所以事务管理是企业级应用程序开发中必不可少的技术。
事务就是一组由于逻辑上紧密关联而合并成一个整体(工作单元)的多个数据库操作,这些操作要么都执行,要么都不执行。
事务的四个关键属性(ACID)
- 原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行。
- 一致性(consistency):“一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的,事务执行之后数据仍然是正确的。如果一个事务在执行的过程中,其中某一个或某几个操作失败了,则必须将其他所有操作撤销,将数据恢复到事务执行之前的状态,这就是回滚。
- 隔离性(isolation):在应用程序实际运行过程中,事务往往是并发执行的,所以很有可能有许多事务同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。隔离性原则要求多个事务在并发执行过程中不会互相干扰。
- 持久性(durability):持久性原则要求事务执行完成后,对数据的修改永久的保存下来,不会因各种系统错误或其他意外情况而受到影响。通常情况下,事务对数据的修改应该被写入到持久化存储器中。
@Transactional注解的定义
@Target({ ElementType.METHOD, ElementType.TYPE }) 可以标注在类上,以及方法上
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
propagation属性:事务的传播行为,一个事务方法被另外一个事务方法调用时,当前的事务如何使用事务.,属性的内容主要先择在一下的Propagation的枚举类中
public enum Propagation {
/**支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是 Spring 默认的事务的传播。*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
/**支持当前事务,如果当前没有事务,就以非事务方式执行*/
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
/**支持当前事务,如果当前没有事务,就抛出异常*/
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
/**新建事务,如果当前存在事务,把当前事务挂起*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
/**-以非事务方式执行操作,如果当前存在事务,就把当前事务挂起*/
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
/**以非事务方式执行,如果当前存在事务,则抛出异常*/
NEVER(TransactionDefinition.PROPAGATION_NEVER),
/**如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。
* 它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。
* 内部事务的回滚不会对外部事务造成影响。
* 它只对DataSourceTransactionManager事务管理器起效
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
private final int value;
Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
isolation属性:事务的隔离级别,I也在org.springframework.transaction.annotation.Isolation枚举类中
public enum Isolation {
/**数据库的默认级别*/
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
/**读未提交 脏读*/
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
/**读已提交 不可重复读(update)*/
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
/**可重复读 幻读(插入操作)*/
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
/** 串行化 效率低*/
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
private final int value;
Isolation(int value) { this.value = value; }
public int value() { return this.value; }
}
timeout:事物的超时时间,设置事务在强制回滚之前可以占用的时间,默认为-1,不超时,单位为s(测试为单位s)
readOnly:是否只读
- true: 只读 ;代表着只会对数据库进行读取操作, 不会有修改的操作,如果确保当前的事务只有读取操作,就有必要设置为只读,可以帮助数据库,引擎优化事务
- false: 非只读 不仅会读取数据还会有修改操作
剩下的四个属性:事务的回滚与不回滚 默认情况下, Spring会对所有的运行时异常进行事务回滚,指定异常的类名,或者类型
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
spring配置
xml文件,基于注解的形式配置
<!-- 事务管理器 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务注解 transaction-manager 用来指定事务管理器, 如果事务管理器的id值 是 transactionManager, 可以省略不进行指定。 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager" />
xml文件,基于方法名的形式配置
<!-- 事务管理器 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 基于xml配置事务管理 事务管理器 事务属性设置 -->
<tx:advice transaction-manager="dataSourceTransactionManager" id="txAdvice">
<!-- 配置事务属性 -->
<tx:attributes>
<!-- 具体的方法使用的事务属性 -->
<tx:method name="editUser" isolation="READ_COMMITTED" propagation="REQUIRES_NEW" read-only="false"
timeout="3" />
<!-- 约定方法的名字 -->
<!-- 查询操作: selectxxx -->
<tx:method name="select*" read-only="true" />
<!-- 修改操作: updatexxx -->
<tx:method name="update*" />
<!-- 除了上述指定的方法之外的所有方法 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 切入点表达式 -->
<aop:pointcut expression="execution(* com.badger.spring.tx.xml.service.*.*(..))" id="txPointCut" />
<!-- 切入点表达式 与 事务配置的结合 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
在springboot中,可以使用@EnableTransactionManagement注解开启事务,
有的事物是自动装配的,例如mybatis,会依赖spring-jdbc
spring-jdbc,会自动装配事务的解析器,有兴趣的,可以直接看下org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration.class以及org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration.class的源代码,就可以了,部分源码
@Configuration
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TransactionManagerCustomizers platformTransactionManagerCustomizers(
ObjectProvider<List<PlatformTransactionManagerCustomizer<?>>> customizers) {
return new TransactionManagerCustomizers(customizers.getIfAvailable());
}
@Configuration
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
public static class TransactionTemplateConfiguration {
private final PlatformTransactionManager transactionManager;
public TransactionTemplateConfiguration(
PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Bean
@ConditionalOnMissingBean
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(this.transactionManager);
}
}
@Configuration
@ConditionalOnBean(PlatformTransactionManager.class)
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration {
@Configuration
@EnableTransactionManagement(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
}
@Configuration
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {
@Configuration
@ConditionalOnSingleCandidate(DataSource.class)
static class DataSourceTransactionManagerConfiguration {
private final DataSource dataSource;
private final TransactionManagerCustomizers transactionManagerCustomizers;
DataSourceTransactionManagerConfiguration(DataSource dataSource,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
this.dataSource = dataSource;
this.transactionManagerCustomizers = transactionManagerCustomizers
.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public DataSourceTransactionManager transactionManager(
DataSourceProperties properties) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(
this.dataSource);
if (this.transactionManagerCustomizers != null) {
this.transactionManagerCustomizers.customize(transactionManager);
}
return transactionManager;
}
}
}