Spring AOP-事务管理

Spring AOP-事务管理,第1张

1、@Transactional使用位置

Ⅰ 写在接口类上,该接口的所有实现类的所有方法都会有事务

Ⅱ 写在接口方法上,该接口的所有实现类的该方法都会有事务;

Ⅰ 写在实现类上,该类中的所有方法都会有事务;

Ⅱ 写在实现类方法上,该方法上有事务。

建议:写在实现类或实现类的方法上。

2、PlatformTransactionManager

PlatformTransactionManager是Spring中的事务管理接口,具体如下:

3、DataSourceTransactionManager

Spring中JDBC事务管理实现类是DataSourceTransactionManager,所以我们使用MyBatis时,如果需要进行事务管理则配置该事务管理即可。

1、基础准备

jdbc.properties如下:

JdbcConfig如下:

2、测试

1、相关注解

配置类注解,定义在配置类上。

设置当前Spring环境中开启注解式事务支持。

接口、类、方法注解,定义在接口、类、方法上。

为当前业务层方法添加事务(如果设置在类或接口上方则类或接口中所有方法均添加事务)。

2、事务角色

发起事务方,在Spring中通常指代业务层开启事务的方法。

加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法。

3、@Transactional常用属性

true只读事务,false读写事务,增删改要设为false,查询设为true。

设置超时时间单位秒,在多长时间之内事务没有提交成功就自动回滚,-1表示不设置超时时间。

当出现指定异常进行事务回滚。

4、事务传播行为

比如上述测试案例中,我们给log方法上的@Transactional设置了传播属性为REQUIRES_NEW,表示当前事务协调员会自己开启一个事务。并不会因为transfer发生回滚而回滚。

Ⅰ REQUIRED(默认);

Ⅱ SUPPORTS;

Ⅲ MANDATORY;

Ⅳ REQUIRES_NEW;

Ⅴ NOT_SUPPORTED;

Ⅵ NEVER;

Ⅶ NESTED。

以上即为Spring AOP-事务管理的全部内容,感谢阅读。

    1、xml文件头部需要添加spring的相关支持:

    2、配置事务管理器

    3、配置需要加入事务的方法规则,或者说是一个切面

<!-- 定义事务管理器 --> 

<bean id="transactionManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 

  <property name="dataSource" ref="dataSource" /> 

</bean> 

<!--使用注释事务 --> 

<tx:annotation-driven  transaction-manager="transactionManager" />

事物配置中有哪些属性可以配置

(1)事务的传播性:@Transactional(propagation=Propagation.REQUIRED) 

      如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)

(2)事务的超时性:@Transactional(timeout=30) //默认是30秒 

      注意这里说的是事务的超时性而不是Connection的超时性,这两个是有区别的

(3)事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED)

      读取未提交数据(会出现脏读, 不可重复读) 基本不使用

(4)回滚:

指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)

指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。

(5)只读:@Transactional(readOnly=true)

该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。

例如:

@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,timeout=1,isolation=Isolation.DEFAULT) 

public void saveUser(Map<String, String>map) throws Exception { 

    System.out.println("方法开始") 

    for (int i = 0i <500000i++) { 

            System.out.println("*") 

        } 

    System.out.println("进入保存") 

    userDao.saveUser(map) 

    System.out.println("退出保存") 

解释说明

事务的传播级别定义的是事务的控制范围,主要是父子事务之间的相互影响关系;事务的隔离级别定义的是事务读写的控制范围,主要是两个事务之间的相互影响关系。

传播级别:

1)、REQUIRED

如果当前方法已经在事务中,那么就以父事务执行,不需要新建事务;如果当前方法不在事务中,那么就为当前方法新建事务。回滚情况:父子方法中任何地方出现问题,都会全部回滚。

2)、SURPPORTED

如果当前方法已经在事务中,那么就以当前事务执行;如果当前方法不再事务中,那么就以非事务方式运行。如果运行在事务中,那么只要出现异常都会回滚。

3)、NOT_SURPPORTED

如果当前方法已经在事务中,那么就挂起当前事务,以非事务方式运行,方法执行完毕后,恢复事务;如果当前方法不再事务中,那么就以非事务方式执行。

4)、MANDATORY

强制以事务方式执行,如果当前方法不在事务中,那么会抛出异常。

5)、NEVER

与MANDATORY相反,强制以非事务方式执行,如果当前方法在事务中,那么会抛出异常。

6)、REQUIRED_NEW

与REQUIRED不同的是,无论该方法当前是不是在事务中,都会为自己新建一个事务。如果当前已经在事务中,那么会挂起父事务,为自己新建一个事务。父事务不影响它子事务的提交和回滚。

7)、NESTED

嵌套事务。理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。而Nested事务的好处是他有一个savepoint。

例如:

ServiceA {

/**

* 事务属性配置为 PROPAGATION_REQUIRED

*/

void methodA() {

try {

//savepoint

ServiceB.methodB()//PROPAGATION_NESTED 级别

} catch (SomeException) {

// 执行其他业务, 如 ServiceC.methodC()

}

}

}

也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如ServiceC.methodC,继续执行,来尝试完成自己的事务。

原文链接:https://blog.csdn.net/wgh1015398431/java/article/details/52861048

你说的事务没有生效具体情况没看运行结果看不出来,我这里说一下我做的测试和理解吧。

首先我这个项目使用了SpringMVC搭建,事务配置和你的基本一致:

因为我的事务管理器使用的id是默认id,所以<tx:annotation-driven />后面不需要跟id

连接池使用的是com.mchange.v2.c3p0.ComboPooledDataSource

然后我查资料了解到:

所以我就不像你这样写了,反正都是默认值

接着做出如下几种测试:

1、在Service实现层方法上添加@Transactional注解:

控制器的注册方法这么写:

可以看到这个是重复插表了,必然引起违反唯一约束异常

把数据库清空:

postman准备:

冲!

好,服务器返回500了,我们看下日志:

第一次insert时,首先创建了一个会话,注册事务,连接入池,执行插入,插入成功,释放会话资源,事务提交,事务关闭。

第二次insert,还是创建会话吧啦吧啦一堆,然后插入失败,服务器发生com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException异常,此时直接释放会话,在服务器打印错误信息,事务关闭。事务没有进行提交,所以事务得到回滚。

但是可以发现因为第一次插入的会话也好,事务也好,已经和第一次插入不是一个了,所以第一次插入还是成功了:

只有第二次事务得到了回滚。

所以我意识到只有将所有数据库 *** 作都包含在同一个事务之中,那么事务回滚的时候才能大家一起回滚。

因此,这次测试的时候,我将@Transactional直接放到控制器中:

再冲!

再次500了,我们看下日志:

第一次插入时,新建了会话,注册了事务,入池,然后执行,执行成功,释放会话资源,然后事务没有关闭,也没有提交。

紧接着就开始第二次插入,它直接fetched(获取)当前事务中的会话,执行插入,释放会话资源,此时再次发生MySQLIntegrityConstraintViolationException异常,打印异常信息,事务没有提交直接关闭,得到回滚。

查了下数据库确实没有新数据被插入。

综上所述,我这个事务回滚还是生效了的,关键还是在于@Transactional注解的设置位置。这个注解设置在某个方法上,只能将这个方法内所进行的所有数据库 *** 作归结到一个事务里,出了这个方法,那就管不了了。

所以我想看一下你的这个注解到底是怎么加的?

另外直接把注解加载服务层的类上我试了也不行

这样也是无法回滚的。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/bake/11403801.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-15
下一篇 2023-05-15

发表评论

登录后才能评论

评论列表(0条)

保存