spring 事物总结

1、什么是事务?

事务是逻辑上的一组操作,要么都执行,要么都不执行。

2、 事务的特性(ACID)了解么?

  • 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  • 一致性: 执行事务前后,数据保持一致;
  • 隔离性: 并发访问数据库时,一个用户的事物不被其他事务所干扰也就是说多个事务并发执行时,一个事务的执行不应影响其他事务的执行;
  • 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

3、详谈 Spring 对事务的支持

3.1. Spring 支持两种方式的事务管理
1).编程式事务管理

通过 TransactionTemplate或者TransactionManager手动管理事务,实际应用中很少使用,但是对于你理解 Spring 事务管理原理有帮助。

使用TransactionTemplate 进行编程式事务管理的示例代码如下:

@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

                try {

                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }

            }
        });
}
ResultDto ret = null;
ret = (ResultDto) this.transactionTemplate.execute(new TransactionCallback() {
	@Override
	public Object doInTransaction(TransactionStatus status) {
		ResultDto ret = null;
		try {
			drillTaskDao.deleteByKey(taskid);
		} catch (Exception e) {
			logger.error("delDrillTask:" + e.getMessage(), e);
			ret = ResultBuilder.buildResult(ResultBuilder.FAIL_CODE, null, ErrorCode.COM_DBDELETEERROR);
			return ret;
		}
		finally {
			status.setRollbackOnly();
		}
		 
		ret = cleartaskrelativedata(taskid, appid, true);
		return ret;
	}
});
return ret;

使用 TransactionManager 进行编程式事务管理的示例代码如下:

@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {

  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....  业务代码
              transactionManager.commit(status);
          } catch (Exception e) {
              transactionManager.rollback(status);
          }
}
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
	private HibernateTransactionManager transactionManager;
	private DefaultTransactionDefinition def;
 
	public HibernateTransactionManager getTransactionManager() {
		return transactionManager;
	}
 
	public void setTransactionManager(HibernateTransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}
 
	public void createTransactionDefinition() {
		def = new DefaultTransactionDefinition();
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
		def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
	}
 
	public void saveOrUpdate(User user) {
		TransactionStatus status = transactionManager.getTransaction(def);
		try {
			this.getHibernateTemplate().saveOrUpdate(user);
		} catch (DataAccessException ex) {
			transactionManager.rollback(status);
			throw ex;
		}
		transactionManager.commit(status);
	}
}
2)声明式事务管理

推荐使用(代码侵入性最小),实际是通过 AOP 实现(基于@Transactional 的全注解方式使用最多)。

使用 @Transactional注解进行事务管理的示例代码如下:

@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
  //do something
  B b = new B();
  C c = new C();
  b.bMethod();
  c.cMethod();
}
3.2. Spring 事务管理接口介绍

Spring 框架中,事务管理相关最重要的 3 个接口如下:

  • PlatformTransactionManager: (平台)事务管理器,Spring 事务策略的核心。
  • TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。
  • TransactionStatus: 事务运行状态。
3.3. 事务属性详解
3.3.1 事物传播行为介绍:
  • @Transactional(propagation=Propagation.REQUIRED)
    如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
  • @Transactional(propagation=Propagation.NOT_SUPPORTED)
    容器不为这个方法开启事务
  • @Transactional(propagation=Propagation.REQUIRES_NEW)
    不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
  • @Transactional(propagation=Propagation.MANDATORY)
    必须在一个已有的事务中执行,否则抛出异常
  • @Transactional(propagation=Propagation.NEVER)
    必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
  • @Transactional(propagation=Propagation.SUPPORTS)
    如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
3.3.2 事务隔离级别:
  • @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    读取未提交数据(会出现脏读, 不可重复读) 基本不使用
  • @Transactional(isolation = Isolation.READ_COMMITTED)
    读取已提交数据(会出现不可重复读和幻读)
  • @Transactional(isolation = Isolation.REPEATABLE_READ)
    可重复读(会出现幻读)
  • @Transactional(isolation = Isolation.SERIALIZABLE)
    串行化

MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED

脏读 : 一个事务读取到另一事务未提交的更新数据
不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说,
后续读取可以读到另一事务已提交的更新数据. 相反, "可重复读"在同一事务中多次
读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据
幻读 : 一个事务读到另一个事务已提交的insert数据

3.3.3 事物超时设置:

@Transactional(timeout=30) //默认是30秒

3.4. @Transactional 注解使用详解
1) @Transactional 的作用范围

方法 :推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,否则不生效。类 :如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。接口 :不推荐在接口上使用。

2) @Transactional 的常用配置参数

@Transactional注解源码如下,里面包含了基本事务属性的配置:

@Target({ElementType.TYPE, ElementType.METHOD})
@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 {};
}

@Transactional 的常用配置参数总结(只列巨额 5 个我平时比较常用的):

属性名说明
propagation事务的传播行为,默认值为 REQUIRED,可选的值在上面介绍过
isolation事务的隔离级别,默认值采用 DEFAULT,可选的值在上面介绍过
timeout事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务
readOnly指定事务是否为只读事务,默认值为 false
rollbackFor用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型
3)@Transactional 事务注解原理

@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。

多提一嘴:createAopProxy() 方法 决定了是使用 JDK 还是 Cglib 来做动态代理,源码如下:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

 @Override
 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
   Class<?> targetClass = config.getTargetClass();
   if (targetClass == null) {
    throw new AopConfigException("TargetSource cannot determine target class: " +
      "Either an interface or a target is required for proxy creation.");
   }
   if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    return new JdkDynamicAopProxy(config);
   }
   return new ObjenesisCglibAopProxy(config);
  }
  else {
   return new JdkDynamicAopProxy(config);
  }
 }
  .......
}

如果一个类或者一个类中的 public 方法上被标注@Transactional 注解的话,Spring 容器就会在启动的时候为其创建一个代理类,在调用被@Transactional 注解的 public 方法的时候,实际调用的是,TransactionInterceptor 类中的 invoke()方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务。

4)Spring AOP 自调用问题

若同一类中的其他没有 @Transactional 注解的方法内部调用有 @Transactional 注解的方法,有@Transactional 注解的方法的事务会失效。
这是由于Spring AOP代理的原因造成的,因为只有当 @Transactional 注解的方法在类以外被调用的时候,Spring 事务管理才生效。
MyService 类中的method1()调用method2()就会导致method2()的事务失效。

@Service
public class MyService {

private void method1() {
     method2();
     //......
}
@Transactional
 public void method2() {
     //......
  }
}

解决办法就是避免同一类中自调用或者使用 AspectJ 取代 Spring AOP 代理。

5) @Transactional 的使用注意事项总结
  • @Transactional 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用;
  • 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效;
  • 正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT界的一只菜鸟

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值