2018-07-05

## 面试题:spring事物失效的9大原因

#### 1.spring事物实现方式及原理

Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring 是无法提供事务功能的。真正的数据库层的事务提交和回滚是在binlog提交之后进行提交的 通过 redo log 来重做, undo log来回滚。

一般我们在程序里面使用的都是在方法上面加`@Transactional ` 注解,这种属于声明式事物。

声明式事务本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

#### 2.数据库本身不支持事物

这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB

#### 3.当前类的调用

```java

@Service

public class UserServiceImpl implements UserService {

    public void update(User user) {

        updateUser(user);

    }


    @Transactional(rollbackFor = Exception.class)

    public void updateUser(User user) {

        // update user

    }


}

```

上面的这种情况下是不会有事物管理操作的。

通过看声明式事物的原理可知,spring使用的是AOP切面的方式,本质上使用的是动态代理来达到事物管理的目的,当前类调用的方法上面加`@Transactional` 这个是没有任何作用的,因为调用这个方法的是`this`.

OK, 我们在看下面的一种例子。

```java

@Service

public class UserServiceImpl implements UserService {

    @Transactional(rollbackFor = Exception.class)

    public void update(User user) {

        updateUser(user);

    }


    @Transactional(propagation = Propagation.REQUIRES_NEW)

    public void updateUser(User user) {

        // update user

    }


}

```

这次在 update 方法上加了 `@Transactional`,updateUser 加了 `REQUIRES_NEW` 新开启一个事务,那么新开的事务管用么?

答案是:不管用!

因为它们发生了自身调用,就调该类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。

#### 4.方法不是public的

```java

@Service

public class UserServiceImpl implements UserService {

    @Transactional(rollbackFor = Exception.class)

    private void updateUser(User user) {

        // update user

    }


}

```

`private` 方法是不会被spring代理的,因此是不会有事物产生的,这种做法是无效的。

#### 5.没有被spring管理

```java

//@Service

public class UserServiceImpl implements UserService {

    @Transactional(rollbackFor = Exception.class)

    public void updateUser(User user) {

        // update user

    }


}

```

没有被spring管理的bean, spring连代理对象都无法生成,当然无效咯。

#### 6.配置的事物传播性有问题

```java

@Service

public class UserServiceImpl implements UserService {

    @Transactional(propagation = Propagation.NOT_SUPPORTED)

    public void update(User user) {

        // update user

    }   

}

```

回顾一下spring的事物传播行为

Spring 事务的传播行为说的是,当多个事务同时存在的时候, Spring 如何处理这些事务的行为。

1. PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

2. PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行

3. PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

4. PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。

5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6. PROPAGATION_NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常。

7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按 REQUIRED 属性执行

当传播行为设置了PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER,PROPAGATION_SUPPORTS这三种时,就有可能存在事物不生效

#### 7.异常被你 "抓住"了

```java

@Service

public class UserServiceImpl implements UserService {

    @Transactional(rollbackFor = Exception.class)

    public void update(User user) {


      try{

        // update user

      }catch(Execption e){

        log.error("异常",e)

      }

    }   

}

```

异常被抓了,这样子代理类就没办法知道你到底有没有错误,需不需要回滚,所以这种情况也是没办法回滚的哦。

#### 8.接口层声明式事物使用cglib代理

```java

public interface UserService  {

    @Transactional(rollbackFor = Exception.class)

    public void update(User user) 

}

```

```java

@Service

public class UserServiceImpl implements UserService {


    public void update(User user) {

        // update user

    }   

}

```

通过元素的 "proxy-target-class" 属性值来控制是基于接口的还是基于类的代理被创建。如果 "proxy-target-class" 属值被设置为 "true",那么基于类的代理将起作用(这时需要CGLIB库cglib.jar在CLASSPATH中)。如果 "proxy-target-class" 属值被设置为 "false" 或者这个属性被省略,那么标准的JDK基于接口的代理将起作用

注解@Transactional cglib与java动态代理最大区别是代理目标对象不用实现接口,那么注解要是写到接口方法上,要是使用cglib代理,这是注解事物就失效了,为了保持兼容注解最好都写到实现类方法上。

#### 9.rollbackFor异常指定错误

```java

@Service

public class UserServiceImpl implements UserService {

    @Transactional

    public void update(User user) {

        // update user

    }   

}

```

上面这种没有指定回滚异常,这个时候默认的回滚异常是`RuntimeException` ,如果出现其他异常那么就不会回滚事物

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

推荐阅读更多精彩内容