Spring,为内部方法新起一个事务,此处应有坑。

  事务的作用,使我们操作能够连贯起来。而spring则是提供了一个更简单的方法,只要使用 @Transactional 一个注解,就可以保证操作的连贯性了。

  普通用法,稍后再说,这里要说的是: 在最外面的方法中,有一个@Transactional 的注解,当有抛出异常时,则进行回滚操作:

@Transactional(readOnly = false, rollbackFor = Throwable.class, isolation = Isolation.REPEATABLE_READ)

  原本这个方法运行得好好的,但是有一天,我们需要在这个方法里添加一个新业务操作,而且这个业务操作是不要求回滚的,类似于做日志记录一类的。WHAT SHOULD I DO ?

  由于业务的独特性,我能够快速想到的是,在这个类里面加一个private方法,然后直接去调用就ok了,如果说还是考虑到回滚的话,我也快速想到 @Transactional 的NOT_SUPPORTED传播特性,如:

@Transactional(propagation = Propagation.NOT_SUPPORTED)
private void doMyExJob(UserDebitCardBean userDebitCard) {
    System.out.println("do my job...");
    //do my job...
}

  这看起来很合理,没毛病。

  然而就是运行不起来,只要外面调用的方法一抛出异常,那么这个新方法的数据操作将会被回滚。妈蛋,到底哪里出了问题???仔细查了下资料,原来 @Transactional 注解由于原理决定了他只能作用于public方法中,而这里改为private,就完全被忽略无视了。OK,改呗:

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void doMyExJob(UserDebitCardBean userDebitCard) {
    System.out.println("do my job...");
    //do my job...
}

  感觉应该好了,然而并没有。我也是醉了,这个问题,如果仔细花时间,找原理是没有问题的,但是在关键时刻来这么一下,还是很不爽的。 网上看到一哥们说,还必须要将方法写到另一个类中,而且要通过spring的注入方式进行调用,才可以。好吧,那我就按照他的来,结果真的成功了。

//在接口中进行了定义,能够注入
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void doMyExJob(UserDebitCardBean userDebitCard) {
    System.out.println("do my job...");
    //do my job...
}

  总算可以了,在赶时间的时候,能够解决问题的,就是好方法。至此,问题解决。1. 使用public访求;2. 写在外部类中,可被调用; 3. 使用注入的方式进行该方法的执行。

  说实话,spring这种事务还是有点不太好用的,要求太多,当然了,有很大部分原因是我没有理解其精髓。OK,下面我们来看看spring事务的讲解:

在配置文件中,默认情况下,<tx:annotation-driven>会自动使用名称为transactionManager的事务管理器。所以,如果定义的事务管理器名称为transactionManager,那么就可以直接使用<tx:annotation-driven/>。如下:

?


1

2

3

4

5

6

7

8

<!-- 配置事务管理器 -->

<beanid="transactionManager"

    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"

    p:dataSource-ref="dataSource">

</bean>

<!-- enables scanning for @Transactional annotations -->

<tx:annotation-driven/>

<tx:annotation-driven>一共有四个属性如下,

  • mode:指定Spring事务管理框架创建通知bean的方式。可用的值有proxy和aspectj。前者是默认值,表示通知对象是个JDK代理;后者表示Spring AOP会使用AspectJ创建代理
  • proxy-target-class:如果为true,Spring将创建子类来代理业务类;如果为false,则使用基于接口的代理。(如果使用子类代理,需要在类路径中添加CGLib.jar类库)
  • order:如果业务类除事务切面外,还需要织入其他的切面,通过该属性可以控制事务切面在目标连接点的织入顺序。
  • transaction-manager:指定到现有的PlatformTransaction Manager bean的引用,通知会使用该引用

@Transactional的属性

  isolation 枚举org.springframework.transaction.annotation.Isolation的值 事务隔离级别

  noRollbackFor Class<? extends Throwable>[] 一组异常类,遇到时不回滚。默认为{}

  noRollbackForClassName Stirng[] 一组异常类名,遇到时不回滚,默认为{}

  propagation 枚举org.springframework.transaction.annotation.Propagation的值 事务传播行为

  readOnly boolean 事务读写性

  rollbackFor Class<? extends Throwable>[] 一组异常类,遇到时回滚

  rollbackForClassName Stirng[] 一组异常类名,遇到时回滚

  timeout int 超时时间,以秒为单位

  value String 可选的限定描述符,指定使用的事务管理器

@Transactional标注的位置
@Transactional注解可以标注在类和方法上,也可以标注在定义的接口和接口方法上。
如果我们在接口上标注@Transactional注解,会留下这样的隐患:因为注解不能被继承,所以业务接口中标注的@Transactional注解不会被业务实现类继承。所以可能会出现不启动事务的情况。所以,spring建议我们将@Transaction注解在实现类上。
在方法上的@Transactional注解会覆盖掉类上的@Transactional。

注意:

  @Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

  虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

  默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

  拼的就是工具的使用熟练度,哦。

时间: 2024-11-07 18:24:53

Spring,为内部方法新起一个事务,此处应有坑。的相关文章

2.2.1 传播行为 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

单独调用methodB方法: main{ metodB(); } 相当于 Main{ Connection con=null; try{ con = getConnection(); con.setAutoCommit(false); //方法调用 methodB(); //提交事务 con.commit(); } Catch(RuntimeException ex) { //回滚事务 con.rollback(); } finally { //释放资源 closeCon(); } } Spri

方法中开启一个事务之后,能否调用另一个通过事务实现的函数?

领悟:一个事务开启,不能再开启另一个事务来源:导入用户时开启一个事务,在此事务中我试图调用曾经写好的Insert添加用户的函 数,Insert中数据库操作在一个事务中,结果系统提 示:SqlMap could not invoke BeginTransaction(). A Transaction is already started. Call CommitTransaction() or RollbackTransaction first.   SqlMap 不能调用BeginTransac

Spring单实例、多线程安全、事务解析

原文:http://blog.csdn.net/c289054531/article/details/9196053 引言: 在使用Spring时,很多人可能对Spring中为什么DAO和Service对象采用单实例方式很迷惑,这些读者是这么认为的: DAO对象必须包含一个数据库的连接Connection,而这个Connection不是线程安全的,所以每个DAO都要包含一个不同的Connection对象实例,这样一来DAO对象就不能是单实例的了. 上述观点对了一半.对的是“每个DAO都要包含一个

【JavaEE】Springmvc+Spring+Hibernate搭建方法及example

前面两篇文章,分别介绍了Springmvc和Spring的搭建方法,本文再搭建hibernate,并建立SSH最基本的代码结构. Hibernate和前面两个比就比较复杂了,Hibernate是一个orm的框架,也就是负责面向对象中的对象(Object)和关系型数据库这个关系(Relation)之间的映射(Mapping).因为关系型数据库的思维方式,和编程的时候对于对象的理解是有偏差的,所以也有一些面向对象的数据库,但是随着这些orm框架的完善,面向对象的数据库就销声匿迹了. 当然,我这篇文章

利用反射模拟一个spring的内部工作原理

这个简单的案例是实行了登录和注册的功能,没有链接数据库. 在bean中id 是唯一的,id和name的区别在于id不能用特殊字符而name可以用特殊字符,比如:/-\.... 1 package com.obtk.reflect; 2 3 public class Logon { 4 /** 5 * 帐号密码长度大于六位字符就成功,否则失败! 6 * */ 7 public String select(String name, String pass) { 8 if (name.length()

spring aop之对象内部方法间的嵌套失效

spring aop之对象内部方法间的嵌套失效 先看一下spring 代理原理: *       spring代理嵌套方法调用不生效 * *       Spring AOP defaults to using standard JDK dynamic proxies for AOP *       proxies. This enables any interface (or set of interfaces) to be *       proxied. * *       Spring

装饰一个类及内部方法

通过装饰器函数修改一个类属性 class MyClass: NAME = 'My CLASS HAHAHA' def __init__(self): pass print(MyClass.__dict__['NAME']) My CLASS HAHAHA 等价于: def setname(name): def warpper(cls): cls.NAME = name return cls return warpper @setname('MY CLASS enen')  # class MyC

Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效)

今天在使用Spring cache的Cacheable注解的过程中遇见了一个Cacheable注解失效的问题,检查问题发生的原因是因为Spring的Cacheable注解是基于Spring AOP实现的,但是类内部方法互相调用时不会被Spring AOP拦截的,所以导致被调用方法的Cacheable注解失效,特此记录. 问题复现 @Service public class UserServiceImpl{ @Override public User detail(Long id) { // 校验

BeginInvoke 方法真的是新开一个线程进行异步调用吗?

转自原文BeginInvoke 方法真的是新开一个线程进行异步调用吗? 参考以下代码: public delegate void treeinvoke(); private void UpdateTreeView() { MessageBox.Show(System.Threading.Thread.CurrentThread.Name); } private void button1_Click(object sender, System.EventArgs e) { System.Threa