什么是事务?
说到事务,一般指的都是数据库中的事务。
- 是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作。
- 这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行。
- 事务是一组不可再分割的操作集合(工作逻辑单元)。
数据具有以下特性(ACID)
- 原子性(Atomicity) 事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
- 一致性(Consistency) 事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
- 隔离性(Isolation) 隔离性是当多个用户并发访问数据库时,比如同时操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
- 持久性(Durability) 事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
事务的隔离级别
- Read uncommitted(未授权读取、读未提交) 如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。
- Read committed(授权读取、读提交 读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
- Repeatable read(可重复读取) 可重复读是指在一个事务内,多次读同一数据,读到的数据结果是一样的。(Mysql默认的实现)
- Serializable(序列化) 它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。
spring实现事务
- TracsactionDefinition定义了事务的一些传播行为,隔离级别
- PlatformTracsactionManager(事务管理器)
- TracsactionStatus用来获取事务的运行状态
Spring事务的转播机制
- REQUIRED(Spring默认的事务传播类型) 如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务。
- SUPPORTS 当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行。
- MANDATORY 当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
- REQUIRES_NEW 创建一个新事务,如果存在当前事务,则挂起该事务。
- NOT_SUPPORTED 始终以非事务方式执行,如果当前存在事务,则挂起当前事务。
- NEVER 不使用事务,如果当前事务存在,则抛出异常。
- NESTED 如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务—调用方可以catch被调用方的异常,这样调用方事务不受影响,被调用方回退。如果调用方出现了异常,那么两个事务都会回退。)
ps:在事务中,如果异常被捕获,是不会发生回退的。Spring事务的隔离级别和数据库的一样,默认隔离级别是跟数据库保持一致。
编程式实现
一、使用DataSourceTransactionManager ,该类实现了PlatformTracsactionManager
1、创建Spring工程时记得导入下面的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.4</version>
</dependency>
2、配置Xml
<context:property-placeholder location="classpath:database.properties"></context:property-placeholder>
<!--数据源-->
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"></property>
<property name="configLocation" value="classpath:mybatis-conf.xml"></property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<property name="basePackage" value="ex.hql.dao"></property>
</bean>
<!-- 配置事务管理器-->
<bean id="transCationManage" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<bean id="template"
class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入连接信息 -->
<property name="dataSource" ref="datasource">
</property>
</bean>
3、代码
public Integer deleteById(int id) {
DefaultTransactionDefinition transactionDefinition=new DefaultTransactionDefinition();
//事务的隔离级别
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
//事务的传播级别
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
transactionManager.setDataSource(jdbcTemplate.getDataSource());
TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
try {
----这里写你的逻辑代码----
//提交事务
transactionManager.commit(status);
}catch (Exception e){
//发生错误时候回滚
transactionManager.rollback(status);
}
二、使用TransactionTemplate
内部有个模板方法
最主要的执行方法还是
可以通过匿名内部类或者Lambada表达式方式实现。
1、声明Bean
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref bean="transCationManage"/>
</property>
</bean>
2、代码
public Object doSomething(){
return transactionTemplate.execute(new TransactionCallback<Object>() {
public Object doInTransaction(TransactionStatus status) {
--这里写你的逻辑代码--
return null;
}
});
}
声明式事务
使用注解 @Transactional
1、配置文件(其他不变)
代理类的话proxy-target-class为true
<tx:annotation-driven transaction-manager="transCationManage" proxy-target-class="true"></tx:annotation-driven>
2、代码
@Transactional
public Object doSomething(){
--写你的逻辑代码--
return null;
}
ps:@Transactional只能作用在public修饰的方法上。
两种方式的比较
编程式的实现方式的粒度更细,使用注解只用作用在方法上,粒度更粗。