Spring事务管理

说起事务,大家应该多多少少用过,尤其是在一个service方法中调用多次dao操作,我们一定要用到事务(@Transational注解),那么这个事务的默认隔离级别和传播机制是什么呢?

先来讲讲脏读 不可重复读 和 幻读。

脏读:我们在并发编程中是很熟悉的,通俗的讲就是你读得数据已经被修改了,已经过时失去意义了。
不可重复读: 同一个事务里面多次读取同一行数据,却返回不同的结果。
幻读:同样一笔查询在整个事务过程中多次执行后,查询所得的结果集不一样。
事务四大特性 ACID

  1. 原子性(Atomicity)

要求事务所包含的全部操作是一个不可分割的整体,如果有一步发生异常,则全部不提交。

  1. 一致性(Consistency)

A给B转钱,A减和B增这两个操作必须保持一致。

  1. 隔离性(Isolation)

事务会将一部分数据与其他事务隔离,防止脏读等。

  1. 持久性(Durability)

事务的结果被写到持久化存储器中。

事务四大隔离级别

隔离级别越高,则性能相对越低,反之亦然。

  1. Read Uncommitted

最低的隔离级别,跟你直译的意思一样:可以读取其它事务未完成的结果。(脏读)

很明显,脏读 不可重复读 和 幻读这三个问题它都有。

  1. Read Committed

大部分数据库采用的默认隔离级别,比上一个隔离级别多了限定:在该事务完成后,才能读取该事务的数据更新后的结果。

它可以避免脏读,但是也有不可重复读取和幻读的问题。

  1. Repeatable Read

可以保证在整个事务的过程中,对同一笔数据的读取结果是相同的,不管其他事务是否同时在对同一笔数据进行更新,也不管其他事务对同一笔数 据的更新提交与否。

Repeatable Read隔离级别避免了脏读和不可重复读取的问题,但无法避免幻读。

  1. Serializable

最为严格的隔离级别,所有的事务操作都必须依次顺序执行,可以避免其他隔离级别遇到的所有问题,是最为安全的隔离级别, 但同时也是性能最差的隔离级别。

通常情况下,我们会使用其他隔离级别加上相应的并发锁的机制来控制对数据的访问,这样既保证 了系统性能不会损失太大,也能够一定程度上保证数据的一致性。

Spring事务传播机制

从JDBC的事务说起

我们都知道,JDBC给我们提供了事务。

1
2
3
4
5
6
7
try{
con.setAutoCommit(false);//开启事务
......
con.commit();//try的最后提交事务
} catch() {
con.rollback();//回滚事务
}
获取事务隔离级别

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
2
3
4
5
6
7
8
public interface PlatformTransactionManager {
// 通过Transation定义 获取Transation
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
// 提交事务
void commit(TransactionStatus var1) throws TransactionException;
// 回滚事务
void rollback(TransactionStatus var1) throws TransactionException;
}
可以看到它里面引用到了TransactionDefinition和TransactionStatus.

TransactionDefinition.java

它里面包含了事务的定义。

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
public interface TransactionDefinition {
// 传播机制
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
// 隔离级别
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;

int getPropagationBehavior();
// 获取隔离级别
int getIsolationLevel();

int getTimeout();

boolean isReadOnly();

@Nullable
String getName();

}
TransactionStatus.java

事务的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface TransactionStatus extends SavepointManager, Flushable {
boolean isNewTransaction();

boolean hasSavepoint();

void setRollbackOnly();

boolean isRollbackOnly();

void flush();

boolean isCompleted();

}
Spring默认事务使用

  1. 代码方式使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Autowired
private PlatformTransactionManager transactionManager;
public void testTX(){
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
try {
// 业务逻辑
// ...

    // 提交事务
    transactionManager.commit(status);
}catch (Exception e){
    // 发生异常,事务回滚
    transactionManager.rollback(status);
}

}

  1. 注解方式使用

1
2
3
4
@Transactional
void testTX2(){
// 业务逻辑 ...
}
这不是玄学,它的底层是依靠AOP动态代理实现,其实重新渲染出的代码和第一个使用方式类似,不过大大减少了开发复杂度。

扩展:@Transactional注解

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
@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 -1;
// 读写或只读事务,默认读写
boolean readOnly() default false;
// 导致事务回滚的异常类数组 
Class<? extends Throwable>[] rollbackFor() default {};
// 导致事务回滚的异常类名字数组
String[] rollbackForClassName() default {};
// 不会导致事务回滚的异常类数组
Class<? extends Throwable>[] noRollbackFor() default {};
// 不会导致事务回滚的异常类名字数组
String[] noRollbackForClassName() default {};

}
Spring事务实践

非入门选手下面的demo可能会引起你的不适(浪费时间)。
假设我要完成一个功能,当删除用户的时候,将与该用户有关的所有数据行都删除。

1
2
3
4
5
6
public void delUser(Integer userId) {
// 删除和用户相关的信息
otherRepository.deleteByUserId(userId);
// 删除用户
userRepository.deleteById(userId);
}
这样的写法一般来讲,会成功的完成任务。但是如果这样一段代码:

1
2
3
4
5
6
7
8
9
public void delUser(Integer userId) {
// 删除和用户相关的信息
otherRepository.deleteByUserId();
if (true) {
throw new RuntimeException("xxx");
}
// 删除用户
userRepository.deleteById(userId);
}
结果会是:deleteByUserId()执行成功,deleteById()执行失败,不满足数据的一致性。

所以我们需要事务来限制:要么全部执行,要么全部不执行(方法中有异常就自动回滚)。那怎么实现呢,只需要在方法上加一个注解:@Transactional

1
2
3
4
5
6
7
8
9
10
@Transactional
public void delUser(Integer userId) {
// 删除和用户相关的信息
otherRepository.deleteByUserId();
if (true) {
throw new RuntimeException("xxx");
}
// 删除用户
userRepository.deleteById(userId);
}
Spring 加载第三方事务管理

比如我有个需求(接着上次的强票系统II),要求信息不能丢失,要用到RabbitMQ的事务管理,那怎么去加载到Spring的事务管理器中呢?

1
2
3
4
5
6
7
8
9
10
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
return connectionFactory;
}

@Bean
public RabbitTransactionManager rabbitTransactionManager(ConnectionFactory connectionFactory) {
return new RabbitTransactionManager(connectionFactory);
}
我们只需要这样做便可以使的使用@Transactional注解来实现对RabbitMQ的事务管理,其它框架也类似。

Spring(72)spring事务(6)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容