Spring--事务管理

事务:程序为了保证业务处理的完整性,执行的一个或多个SQL语句

管理:对事务中的SQL语句进行提交或回滚,叫做事务管理

什么时候使用?主要作用于Service的方法上

事务回顾:

--oracle:commit/rollback   DML操作

--JDBC:自动commit

例如:转账:A账户转1000到B账户

  A账户减少1000(update)

  B账户增加1000(update)

设置:conn.setAutoCommit(false),最后commit/rollback

-------------------------------------------------------------------------------

使用案例:

public void someServiceMethod(){

try{

   //开启事务

service.shareNote()

//事务提交

}catch{

//异常回滚

可以通过spring进行对事务的管理,不用对每一service进行修改,通过AOP进行管理(项目中每一个Dao都是一个事务)

-------------------------------------------------------------------------------

步骤:

1.配置Spring-transaction.xml

2.使用在Service层@Transactional标记

-------------------------------------------------------------------------------

可读可写:

service层中的SQL语句为select

@Transactional(readOnly=true)  设置为只读,在性能上有一定帮助

 

回滚特性:

默认RuntimeException回滚,其他异常不回滚,若要回滚其它异常则@Transactional(rollbackFor=IOException)

public void f1(){

//DB操作(insert)

//IOException

 

传播特性:

默认类型:REQUIRED

REQUIRED--支持当前事务,如果没有当前事务,就新建一个事务,这时最常见的选择

SUPPORTS--支持当前事务,如果没有当前事务,就以非事务方式执行

MANDATORY--支持当前事务,如果没有当前事务,就抛出异常

 

例如:

@Transactional

public void f1(){

//业务代码A

f2();

//若使用默认类型:REQUIRED,当f2()方法报错,则f2()方法回滚,f1()方法回滚

//

//

//业务代码B

 

public void f2(){

//业务代码C

 

隔离特性:

默认属性:READ_COMMITED

针对事务并发进行处理

脏读/幻读

=======================================================

利用AOP处理事务称为声明事务处理(AOP可以添加其他功能,也可以用来处理事务)

使用事务可简化try...catch操作

例如:批量删除笔记业务

配置文件:只需要两个标签即可

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context" 
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"  
	xmlns:jee="http://www.springframework.org/schema/jee" 
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop" 
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
	
	<!-- Spring事务管理 通过AOP实现的 -->
	
	<!-- 定义事务管理Bean  必须配置事务管理器属性, 其值是一个Bean的ID -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dbcp"/><!-- 来源于spring mybatis配置文件中的连接池 -->
	</bean>
	
	<!-- 开启 @Transactional标记 将标记作用在具体方法上-->
	<tx:annotation-driven transaction-manager="txManager"/>
	
	<!-- Spring 的事务管理,是 AOP 的应用,将事务作为切面织入到了 Service 层的业务方法中 -->
</beans>

Dao接口:

//彻底删除笔记(单条删除,并不是修改笔记状态)
public int deleteNote(String id);

-------------------------------------------------------------------------------

Mapper文件:

<!-- //彻底删除笔记(单条删除,并不是修改笔记状态) -->
  <delete id="deleteNote" parameterType="string">
    delete from cn_note
    where
    cn_note_id = #{id}
  </delete>

-------------------------------------------------------------------------------

业务层接口:

//spring事务彻底删除笔记(单条删除,并不是修改笔记状态)String...表示动态参数,就是String[] 数组
public void deleteNotes(String... ids);

-------------------------------------------------------------------------------

业务层实现类:

//spring事务彻底删除笔记(单条删除,并不是修改笔记状态)String...表示动态参数,就是String[] 数组
//要么全部删除,要么都不删除,回滚
@Transactional
public void deleteNotes(String... ids){
	for(String id : ids){
		int n = dao.deleteNote(id);
		if(n != 1){
			//删除笔记失败,抛出异常,触发事务的回滚
			throw new RuntimeException("删错了");
		}
	}
}

-------------------------------------------------------------------------------

测试业务层代码:

@Test//测试使用事务批量删除笔记(本质是delete笔记)
public void testDeleteNotes(){
	//调用动态参数的时候,可以不创建数组,直接写参数
	//String ids = {"id1","id2"};可以不用创建数组
	//抛出异常后回滚
	service.deleteNotes(
			"fd39f2e4d0df435281aaa4c29aecbc85",
			"12121212");
}

由于第二个笔记的ID是错误的,所以会回滚,其次在前代码使用了AOP,输出业务层方法的耗时,对抛出的异常进行写日志,这些AOP切面编程不会对事务产生影响,但是要注意在这些AOP中,若对异常进行了处理,一定要再次抛出,否则@Transactional不会回滚,因为要捕获到异常后才回滚

例如分享笔记的service层,代码大致如下:

//分享笔记
@Override
@Transactional//开启事务
public NoteResult<Object> shareNote(String noteId) {
	NoteResult<Object> result = new NoteResult<Object>();
	//根据noteId查询出Note
	Note note = noteDao.findByNoteId(noteId);
	Share share = new Share();
	share.setCn_share_id(NoteUtil.createId());
	share.setCn_share_title(note.getCn_note_title());
	share.setCn_share_body(note.getCn_note_body());
	share.setCn_note_id(noteId);
	dao.save(share);
	
	//模拟异常 AOP的使用  报异常后会根据事务回滚
	String str = null;
	str.length();
	//模拟异常
	
	result.setStatus(0);
	result.setMsg("分享笔记成功!");
	return result;
}

此处会抛出空指针异常,属于RuntimeException,根据AOP事务控制是应该回滚的,save()方法执行的insert语句回滚,但是在AOP中切入了性能审计的切面代码如下:

//作用:性能审计service层
@Around("within(cn.tedu.cloud_note.service..*)")
public Object audit(ProceedingJoinPoint point){//方法的返回值和参数固定不变
	Object obj = null;//obj用于接收point对象调用后的结果,
	try {
		long t1 = System.currentTimeMillis();
		obj = point.proceed();//相当于调用了service层
		long t2 = System.currentTimeMillis();
		
		String str = point.getSignature().toString();
		
		System.out.println(str+"耗时: "+(t2 - t1)+" ms");
	} catch (Throwable e) {
		e.printStackTrace();
	}
	return obj;
}

代码中捕获了该异常,但是是没处理该异常,包括AOP事务中抛出的RuntimeException,这是应该将其抛出,才会使事务回滚

//性能审计service层
@Around("within(cn.tedu.cloud_note.service..*)")
public Object audit(ProceedingJoinPoint point) throws Throwable{//方法的返回值和参数固定不变
	Object obj = null;
	try {
		long timeStart = System.currentTimeMillis();
		obj = point.proceed();
		long timeEnd = System.currentTimeMillis();
		String str = point.getSignature().toString();
		System.out.println(str+"耗时:"+(timeEnd - timeStart));
	} catch (Throwable e) {
		e.printStackTrace();
		throw e;
	}
	return obj;
}

其他事务相关知识:

(1)使用 Spring 的事务注解管理事务是通过@Transactional 的注解完成的,也可将事务织入到相应方法中。而使用注解方式,只需在配置文件中加入一个 tx 标签,以告诉 spring 使用注解来完成事务的织入。该标签只需指定一个属性,事务管理器,代码如下:

<!-- 事务 -->
<!-- 注册事务管理器 -->
<bean id="myTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="myDataSource"/>
</bean>
<!-- 开启注解驱动 -->
<tx:annotation-driven transaction-manager="myTxManager"/>

(2)只有非只读事务才能回滚的,只读事务是不会回滚的

(3)如果在Service层用了try catch,在catch里面再抛出一个 RuntimeException异常,这样出了异常才会回滚

(4)如果你不喜欢(3)的方式,你还可以直接在catch后面写一句回滚代码

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()

来实现回滚,这样的话,就可以在抛异常后也能return 返回值;比较适合需要拿到Service层的返回值的场景。具体的用法可以参见考下面的伪代码

/** TransactionAspectSupport手动回滚事务:*/
@Transactional(rollbackFor = { Exception.class })  
public boolean test() {  
	try {  
	   doDbSomeThing();    
	} catch (Exception e) {  
		 e.printStackTrace();     
		 //就是这一句了, 加上之后抛了异常就能回滚(有这句代码就不需要再手动抛出运行时异常了)
		 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();  
		 return false;
	}  
   return true;
}

(5)MySQL的MyISAM数据库引擎,不支持ACID(不能回滚), InnoDB 支持ACID,本案例采用的mysql是5.0版本,默认InnoDB 引擎,支持事务回滚 (查看引擎见博客)

总结:Spring 的事务管理,是 AOP 的应用,将事务作为切面织入到了 Service 层的业务方法中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

荒--

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

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

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

打赏作者

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

抵扣说明:

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

余额充值