0%

spring事务

spring事务

事务介绍

一个事务要么同时成功,要么同时失败

特性

  • Atomic原子性 事务是由一个或多个活动组成的一个工作单元。原子性确保事务中的所有操作全部发生或全部不发生

  • Consistent一致性 一旦事务完成,系统必须确保它所建模的业务处于一致的状态

  • Isolated隔离性 事务允许多个用户对数据进行操作,每个用户的操作不会与其他用户纠缠在一起,多个事务之间数据要相互隔离

  • Durable持久性 一旦事务完成,事务的结果应该持久化

事务隔离级别

  • DEFAULT 使用底层数据库预设的隔离层级
  • READ_UNCOMMITTED (读未提交的数据) 允许事务读取其他并行的事务还没提交的数据,脏读、不可重复读、幻读问题都存在
  • READ_COMMITTED(读已提交的数据) 只允许事务读取其他并行事务提交的数据,可以避免脏读,但是不可重复读和幻读仍存在
  • REPEATABLE_READ(可重复读) 确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读仍存在(Mysql默认的事务隔离级别)
  • SERIALIZABLE(串行化) 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,所有并发问题都可以避免,但是性能低下

脏读: 一个未提交事务读取到另一个未提交事务的数据,读得数据已经被修改了,已经过时失去意义了

不可重复读: 一个未提交事务读取到另一个提交事务修改的数据,同一个事务里面多次读取同一行数据,却返回不同的结果

幻读:一个未提交事务读取到另一个提交事务添加的数据,同样一个查询在整个事务过程中多次执行后,查询所得的结果集不一样

传播行为

  • PROPAGATION_REQUIRED 支持当前事务,如果当前没有事务,就新建一个事务;如果有事务,就在这个事务内运行

  • PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行;如果有事务,就在这个事务内运行

  • PROPAGATION_MANDATORY 支持当前事务,如果当前没有事务,就抛出异常。配置该传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。

  • PROPAGATION_REQUIRED_NEW 新建事务,如果当前存在事务,把当前事务挂起

  • PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。可以缩小事务

  • PROPAGATION_NEVER 以非事务方式执行操作,如果当前存在事务,则抛出异常

  • PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行,被嵌套的事务可从当前事务中单独地提交或回滚;如果当前没有事务,则新建事务

只读

事务只进行读取操作

readOnly=true 告诉spring当前事务只会进行读取操作,不会进行修改操作,可以帮助数据库引擎优化

注:如果设置为只读的话,千万不要在事务里修改数据,使用只读操作时,spring不会进行加锁处理,如果修改数据的话,会出现问题

事务超时

事务时间过长,则回滚,默认是-1

回滚规则

默认是发生运行时异常回滚

rollback-for 指事务对于哪些异常应当回滚而不提交(默认spring会对所有的运行时异常回滚)

no-rollback-for 指事务对于哪些异常继续执行不回滚

spring事务的使用

sprin的事务处理其实就是通过AOP来实现的,使用

1
2
3
4
5
6
7
8
create table user(
id int primary key AUTO_INCREMENT,
name varchar(20) not null,
account double
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into user (name,account) values('张三',1000);
insert into user (name,account) values('李四',1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 事务 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.29.RELEASE</version>
</dependency>

<!-- 事务管理器以及数据源 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.29.RELEASE</version>
</dependency>

<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>

事务管理器

spring使用事务管理器来进行事务的管理,最底层的接口是PlatformTransactionManager,主要作用是为应用程序提供事务界定的统一方式,通过该接口,spring为各个平台都提供了对应的事务管理器

1
2
3
4
5
6
7
8
9
10
11
public interface PlatformTransactionManager {
// 根据指定的传播行为,该方法返回当前活动事务或创建一个新的事务
// TransactionDefinition是事务的定义信息,包括隔离级别、传播行为、超时、只读等设置
// TransactionStatus事务的状态,包括是否为新事务、是否已提交、是否有保存点、是否回滚等
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 提交给定的事务和状态
void commit(TransactionStatus status) throws TransactionException;
// 回滚给定事务
void rollback(TransactionStatus status) throws TransactionException;

}

关键类TransactionAspectSupport

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 定义了事务属性
public interface TransactionDefinition {
// 返回传播行为
default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}

// 返回该事务独立于其他事务的工作的程度
default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}

// 返回以秒为单位的时间间隔,事务必须在该时间间隔内完成
default int getTimeout() {
return TIMEOUT_DEFAULT;
}

// 该事务是否是只读的
default boolean isReadOnly() {
return false;
}

// 返回事务的名称
@Nullable
default String getName() {
return null;
}


static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}

}

根据不同的持久化框架提供不同的实现类

  • 使用mybatis时,实现类为DataSourceTransactionManager
  • 使用Hibernate时,实现类为HibernateTransactionManager
  • 使用JPA时,实现类为JpaTransactionManager

使用注解

在这里采用的是spring中的数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/spring?useUnicode=true&amp;characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

当然由于是使用的注解,不要忘记组件扫描

1
<context:component-scan base-package="com.zhanghe.study.spring4.beans.tx"/>

之后就可以在要保证事务的方法上配置@Transactional以及在该注解上配置相应的事务隔离级别(isolation)、事务传播行为(propagation)、对哪些异常执行回滚(rollbackFor)以及不执行回滚(noRollbackFor) 默认对运行时异常回滚

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@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 {};
}

使用XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/spring?useUnicode=true&amp;characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- 配置事务通知 使用xml配置事务属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 根据方法名指定特定的事务属性 -->
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="save*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>


<aop:config>
<!-- 配置事务切入点 -->
<aop:pointcut id="pointCut" expression="execution(* com.zhanghe.study.spring4.beans.tx.UserService.*(..))"/>
<!-- 配置切面 把事务切入点和通知关联起来-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"/>
</aop:config>

分布式事务

随着业务越来越庞大,有时候可能会采取分库分表的方式来进行处理,那么如果数据都不在一个数据库了,如何进行事务处理呢?那就用到了分布式事务,java的事务规范中提出了JTA来进行分布式事务的处理,而在spring中进行了JTA的实现,在springboot中可以使用spring-boot-starter-jta-atomikos来进行实现

欢迎关注我的其它发布渠道