目录标题
一、spring 事务简介
Spring 本身并不实现事务,Spring事务的本质,还是底层数据库对事务的支持,没有 数据库事务的支持,Spring事务就不会生效。
spring 事务中指的提交和回滚实际上是对数据库的提交和回滚操作。
spring 事务说白了就是简化了操作数据库事务的步骤:
- 正常通过jdbc连接数据库,后对事务的操作如下:
-
获取连接 Connection con = DriverManager.getConnection()
-
开启事务con.setAutoCommit(true/false);
-
执行CRUD
-
提交事务/回滚事务 con.commit() / con.rollback();
-
关闭连接 conn.close();
- 使用spring 为我们提供的事务操作:
Spring事务的本质 其实就是 AOP 和 数据库事务,Spring 将数据库的事务操作提取为 切面,通过AOP的方式 增强 事务方法。
当然具体的操作方式可以分为三种,它们都能实现事务:
- Spring的编程式的事务管理(说白了就是通过存纯java代码实现)
- 使用XML配置声明式事务(通过配置文件spring.xml 来实现)
- 使用注解配置声明式事务(使用注解实现,这种方式最简单,只需要一个注解就能实现,推荐使用)
注意:只讲使用,想知道spring 怎么实现事务的可以观看这篇文章:Spring 事务实现分析
二、几个概念
1、PlatformTransactionManager事务管理器(平台事务管理器)
Spring并不直接管理事务,而是提供了多种事务管理器,它们将事务管理的职责委托给JTA或其他持久化机制所提供的平台相关的事务实现。每个事务管理器都会充当某一特定平台的事务实现的门面,这使得用户在Spring中使用事务时,几乎不用关注实际的事务实现是什么。
Spring提供了许多内置事务管理器实现:
-
DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理;
-
JdoTransactionManager:位于org.springframework.orm.jdo包中,提供对单个javax.jdo.PersistenceManagerFactory事务管理,用于集成JDO框架时的事务管理;
-
JpaTransactionManager:位于org.springframework.orm.jpa包中,提供对单个javax.persistence.EntityManagerFactory事务支持,用于集成JPA实现框架时的事务管理;
-
HibernateTransactionManager:位于org.springframework.orm.hibernate3包中,提供对单个org.hibernate.SessionFactory事务支持,用于集成Hibernate框架时的事务管理;该事务管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate 3.2+版本;
-
JtaTransactionManager:位于org.springframework.transaction.jta包中,提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器;
-
OC4JjtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对OC4J10.1.3+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
-
WebSphereUowTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对WebSphere 6.0+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
-
WebLogicJtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对WebLogic 8.1+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持。
举例:
<!--配置事务管理器,需要一个数据源。注意:操作数据库的方式(JdbcTemplate、iBATIS或MyBatis)不同,事务管理器就不一样-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
2、TransactionStatus 事务状态
TransactionStatus代表一个事务的具体运行状态。事务管理器通过该接口获取事务的运行期的状态信息。
该接口继承SavepointManager接口。
- SavepointManager接口拥有以下的方法
-
Object createSavepoint():创建一个保存点对象(回滚点)。
-
void rollbackToSavepoint(Object savepoint):回滚到指定的保存点
-
void releaseSavepoint(Object savepoint):释放一个保存点。如果事务提交,所有保存点会自动释放。
- TransactionStatus扩展了SavepointManager并提供了以下的方法
-
boolean hasSavepoint():判断当前的事务是否有保存点。
-
boolean isNewTransaction():判断当前的事务是否是一个新的事务。
-
boolean isCompleted():判断当前事务是否已经结束:已经提交或者回滚
-
boolean isRollbackOnly():判断当前事务是否已经被标识为rollback-only
-
void setRollbackOnly():将当前事务设置为rollback-only,通过该标识通知事务管理器只能进行事务回滚。
3、TransactionDefinition 事务属性(重要)
3.1、事务隔离级别(isolation)
- 默认(DEFAULT)
使用底层数据存储的默认隔离级别。 所有其他级别对应于 JDBC 隔离级别
//注解:
@Transactional(isolation = Isolation.DEFAULT)
//配置xml,name="transfer":需要增加(开启)事务的方法;isolation="DEFAULT" 隔离级别(默认)
<tx:method name="transfer" isolation="DEFAULT" />
- 读未提交(READ_UNCOMMITTED):
指示可能发生脏读、不可重复读和幻读的常量。 此级别允许由一个事务更改的行在提交该行中的任何更改之前被另一个事务读取(“脏读”)。 如果任何更改被回滚,则第二个事务将检索到无效行。读未提交(顾名思义,一个事务读到另一个事务没有提交的数据(脏数据),这个过程交脏读。读未提交不能解决任何事务中出现的问题。)
//注解:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
//配置xml,name="transfer":需要增加(开启)事务的方法;isolation="READ_UNCOMMITTED" 隔离级别(读未提交)
<tx:method name="transfer" isolation="READ_UNCOMMITTED" />
- 读已提交(READ_COMMITTED):
解决脏读; 可能发生不可重复读和幻读。
此级别仅禁止事务读取其中包含未提交更改的行。
//注解:
@Transactional(isolation = Isolation.READ_COMMITTED)
//配置xml,name="transfer":需要增加(开启)事务的方法;isolation="READ_COMMITTED" 隔离级别(读已提交)
<tx:method name="transfer" isolation="READ_COMMITTED" />
- 可重复读(REPEATABLE_READ):
表示防止脏读和不可重复读; 可能会发生幻读。
此级别禁止事务读取未提交更改的行,也禁止一个事务读取一行,第二个事务更改该行,第一个事务重新读取该行,第二个获取不同值的情况时间(“不可重复读取”)。
不可重复读一般由修改数据造成。
//注解:
@Transactional(isolation = Isolation.REPEATABLE_READ)
//配置xml,name="transfer":需要增加(开启)事务的方法;isolation="REPEATABLE_READ" 隔离级别(可重复读)
<tx:method name="transfer" isolation="REPEATABLE_READ" />
- 串行(SERIALIZABLE):
指示防止脏读、不可重复读和幻读的常量。 该级别包括ISOLATION_REPEATABLE_READ的禁止,并进一步禁止这样一种情况:一个事务读取满足WHERE条件的所有行,第二个事务插入满足WHERE条件的行,第一个事务为相同条件重新读取,检索额外的“第二次阅读中的幻影”行。
幻读一般由新增、删除造成。
//注解:
@Transactional(isolation = Isolation.SERIALIZABLE)
//配置xml,name="transfer":需要增加(开启)事务的方法;isolation="SERIALIZABLE" 隔离级别(串行)
<tx:method name="transfer" isolation="SERIALIZABLE" />
3.2、事务管理器(transactionManager)
事务管理器的概念已经上面已经解释了。事务管理器需要与我们操作数据库的方式一致(事务管理器,顾名思义对我们数据库事务进行管理,也就是事务的提交和回滚,但是不同的数据库操作方式(JDBC抽象框架、MyBatis、Hibernate)对事务的提交、回滚等操作的封装方式不同所有需要指定不同的事务管理器进行管理事务),也需要我们指定和配置的。如下:
<bean id = "datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
<!--配置事务管理器,需要一个数据源。注意:操作数据库的方式(JdbcTemplate、iBATIS或MyBatis)不同,事务管理器就不一样-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!--注解驱动,对添加@Transactional注解的Bean织入事务管理,peoxy-target-class属性如果为true,使用CGLib-->
<tx:annotation-driven transaction-manager="transactionManager" />
3.3、事务传播类型(propagation)
事务的传播性一般用在事务嵌套的场景,比如一个事务方法里面调用了另外一个事务方法,那么两个方法是各自作为独立的方法提交还是内层的事务合并到外层的事务一起提交,这就是需要事务传播机制的配置来确定怎么样执行。
一般来说都是业务层调用数据层,但是也有特殊情况业务层之间相互调用。当业务层相互调用时,该怎么处理业务层之间的事务关系。
spring 事务帮我们封装好了一些常用的处理方式(事务传播类型),如下:
- PROPAGATION_REQUIRED
Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行。 - PROPAGATION_REQUES_NEW
该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可 - PROPAGATION_SUPPORT
如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
PROPAGATION_NOT_SUPPORT
该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码 - PROPAGATION_NEVER
该传播机制不支持外层事务,即如果外层有事务就抛出异常 - PROPAGATION_MANDATORY
与NEVER相反,如果外层没有事务,则抛出异常 - PROPAGATION_NESTED
该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。
举例:
//注解:
@Transactional(propagation = Propagation.REQUIRED)
//配置xml
<tx:method name="*" propagation="REQUIRED" />
3.4、超时时间(timeout)
这个很好理解,也就是调用数据层时,等待返回结果的时间。超时就抛出异常,结束调用。
默认为:使用底层事务系统的默认超时,如果不支持超时,则不使用。
单位为:秒
举例:
//注解:
//-1 使用底层事务系统的默认超时,如果不支持超时,则不使用。
@Transactional(timeout = -1)
//配置xml
<!--为全部方法配置属性-->
<tx:method name="*" timeout="-1" propagation="REQUIRED" />
3.5、只读(readOnly)
如果事务实际上是只读的,则可以将其设置为true布尔标志,从而允许在运行时进行相应的优化。
默认为false 。
这只是对实际事务子系统的提示; 它不一定会导致写访问尝试失败。 无法解释只读提示的事务管理器在请求只读事务时不会抛出异常,而是默默地忽略该提示。
//注解
@Transactional(readOnly = true)
3.6、筛选出现异常哪些后导致事务回滚( rollbackFor、noRollbackFor)
- rollbackFor
定义零 (0) 个或多个异常classes ,它们必须是Throwable子类,指示哪些异常类型必须导致事务回滚。
默认情况下,事务将在RuntimeException和Error上回滚,但不会在已检查的异常(业务异常)上回滚。 - rollbackForClassName
定义零 (0) 个或多个异常名称(对于必须是Throwable子类的异常),指示哪些异常类型必须导致事务回滚。
这可以是完全限定类名的子字符串,目前不支持通配符。 例如, "ServletException"的值将匹配javax.servlet.ServletException及其子类。
注意:仔细考虑模式的具体程度以及是否包含包信息(这不是强制性的)。 例如, “Exception"几乎可以匹配任何内容,并且可能会隐藏其他规则。 如果"Exception"旨在为所有已检查的异常定义规则,则"java.lang.Exception"将是正确的。 对于更不寻常的Exception名称,例如"BaseBusinessException” ,则无需使用 FQN - noRollbackFor
定义零 (0) 个或多个异常Classes ,它们必须是Throwable子类,指示哪些异常类型不得导致事务回滚。 - noRollbackForClassName
定义零 (0) 个或多个异常名称(对于必须是Throwable子类的异常),指示哪些异常类型不得导致事务回滚。
@Transactional(rollbackFor = Exception.class)
@Transactional(rollbackForClassName= Exception.class)
@Transactional(noRollbackFor= IOException.class)
4、其他
4.1、回滚规则
在默认设置下,事务只在出现运行时异常(runtime exception)时回滚,而在出现受检查异常(checked exception)时不回滚(这一行为和EJB中的回滚行为是一致的)。
不过,可以声明在出现特定受检查异常时像运行时异常一样回滚。同样,也可以声明一个事务在出现特定的异常时不回滚,即使特定的异常是运行时异常。
三、使用
导入pom依赖(注意版本,版本不一致会报错)
<!--注意:直接复制否则会出现版本冲突问题-->
<dependencies>
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--aop、切面-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<!-- <scope>test</scope>-->
</dependency>
</dependencies>
1、编程式(了解即可)
数据层
接口
package com.lihua.spring事务_编程式01.mapper;
/**
* 转账操作
* @author 15594
*/
public interface BankMapper {
/**
* 收款人余额增加余额操作
*
* @param name 收款人
* @param money 收款金额
* */
public void addMoney(String name,Double money);
/**
* 付款人余额减少余额操作
*
* @param name 付款人
* @param money 付款金额
* */
public void reduceMoney(String name,Double money);
}
实现类
package com.lihua.spring事务_编程式01.mapper.impl;
import com.lihua.spring事务_编程式01.mapper.BankMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
*
* @author 15594
*/
public class BankMapperImpl extends JdbcDaoSupport implements BankMapper {
public void addMoney(String name, Double money) {
String sql="update bank_card set money= money+ ? where username=?";
this.getJdbcTemplate().update(sql, money,name);
}
public void reduceMoney(String name, Double money) {
String sql="update bank_card set money= money- ? where username=?";
this.getJdbcTemplate().update(sql, money,name);
}
}
业务层(核心代码)
接口
package com.lihua.spring事务_编程式01.service;
/**
* @author 15594
*/
public interface BankService {
/**
* 转账业务
* @param payee 收款人
* @param payer 付款人
* @param money 转账金额
* */
void transfer(String payee,String payer,Double money);
}
实现类
package com.lihua.spring事务_编程式01.service.impl;
import com.lihua.spring事务_编程式01.mapper.BankMapper;
import com.lihua.spring事务_编程式01.service.BankService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Resource;
/**
* @author 15594
*/
@Service("bankService")
public class BankServiceImpl implements BankService {
@Resource(name = "template")
private TransactionTemplate template;
@Autowired
private BankMapper bankMapper;
public void transfer(final String payee, final String payer, final Double money) {
//没有事务
//收钱
// bankMapper.addMoney(payee,money);
// //收钱了,因为异常却没有扣钱
// int i =1/0;
// //付钱
// bankMapper.reduceMoney(payer,money);
//开启事务,
template.execute(new TransactionCallbackWithoutResult() {
//因为没有返回结果,将转账的具体逻辑放在TransactionCallbackWithoutResult接口中
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//收钱
bankMapper.addMoney(payee,money);
//spring 事务默认出现运行时异常回滚事务
//int i =1/0;
//付钱
bankMapper.reduceMoney(payer,money);
}
});
}
}
spring.xml配置
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--包扫面-->
<context:component-scan base-package="com.lihua.spring事务_编程式01"/>
<!--开启注解支持-->
<context:annotation-config></context:annotation-config>
<!--当然我们也可以根据外部的配置文件(key-value格式的文件,比如yml、properties)加载数据源-->
<context:property-placeholder location="classpath:db.properties"/>
<bean id = "datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
<!--实例事务管理器,需要一个数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!--实例TransactionTemplate模板类,需要一个事务管理器-->
<bean id="template" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<!--实例bankDao,需要数据源-->
<bean id="bankDao" class="com.lihua.spring事务_编程式01.mapper.impl.BankMapperImpl">
<property name="dataSource" ref="datasource"></property>
</bean>
</beans>
测试
package com.lihua.spring事务_编程式01.test;
import com.lihua.spring事务_编程式01.service.BankService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 15594
*/
public class Test {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-transaction1.xml");
BankService service=(BankService) ac.getBean("bankService");
service.transfer("李华", "小明", 200.0);
}
}
2、声明式
数据层
接口
package com.lihua.spring事务_声明式02.mapper;
/**
* 转账操作
* @author 15594
*/
public interface BankMapper {
/**
* 收款人余额增加余额操作
*
* @param name 收款人
* @param money 收款金额
* */
public void addMoney(String name, Double money);
/**
* 付款人余额减少余额操作
*
* @param name 付款人
* @param money 付款金额
* */
public void reduceMoney(String name, Double money);
}
实现类
package com.lihua.spring事务_声明式02.mapper.impl;
import com.lihua.spring事务_声明式02.mapper.BankMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
/**
* @author 15594
*/
@Repository("bankMapper")
public class BankMapperImpl implements BankMapper {
@Autowired
private JdbcTemplate jdbcTemplate;
public void addMoney(String name, Double money) {
String sql="update bank_card set money= money+ ? where username=?";
jdbcTemplate.update(sql, money,name);
}
public void reduceMoney(String name, Double money) {
String sql="update bank_card set money= money- ? where username=?";
jdbcTemplate.update(sql, money,name);
}
}
业务层
接口
package com.lihua.spring事务_声明式02.service;
/**
* @author 15594
*/
public interface BankService {
/**
* 转账业务
* @param payee 收款人
* @param payer 付款人
* @param money 转账金额
* */
void transfer(String payee, String payer, Double money);
}
实现类
package com.lihua.spring事务_声明式02.service.impl;
import com.lihua.spring事务_声明式02.mapper.BankMapper;
import com.lihua.spring事务_声明式02.service.BankService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Resource;
/**
* @author 15594
*/
@Service("bankService")
public class BankServiceImpl implements BankService {
@Autowired
@Qualifier("bankMapper")
private BankMapper bankMapper;
public void transfer(final String payee, final String payer, final Double money) {
//收钱
bankMapper.addMoney(payee,money);
//spring 事务默认出现运行时异常回滚事务
int i =1/0;
//付钱
bankMapper.reduceMoney(payer,money);
}
public String aa() {
return "123";
}
}
spring.xml配置(核心代码)
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--包扫面-->
<context:component-scan base-package="com.lihua.spring事务_声明式02"/>
<!--开启注解支持-->
<context:annotation-config></context:annotation-config>
<!--当然我们也可以根据外部的配置文件(key-value格式的文件,比如yml、properties)加载数据源-->
<context:property-placeholder location="classpath:db.properties"/>
<bean id = "datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
<!--配置事务管理器,需要一个数据源。注意:操作数据库的方式(JdbcTemplate、iBATIS或MyBatis)不同,事务管理器就不一样-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!--jdbcTemplate模板类,需要一个数据源。注意:jdbcTemplate必须是事务管理器支持的-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="datasource"></property>
</bean>
<!--事务增强,原来的转账业务中是没有事务的,我们通过AOP给转账业务织入一个事务增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--配置事务的属性-->
<tx:attributes>
<!--为transfer的方法配置事务属性-->
<tx:method name="transfer" isolation="DEFAULT" read-only="false" />
<!--为aa方法配置属性-->
<tx:method name="aa" isolation="READ_COMMITTED" read-only="false" />
<!--为全部方法配置属性-->
<tx:method name="*" timeout="-1" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!--给转账业务织入一个事务增强-->
<aop:config>
<!--配置切面(指定增强和切点)注意这里是advisor(专门处理事务的切面),不是 aspect。-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lihua.spring事务_声明式02.service.impl.BankServiceImpl.*(..))" />
</aop:config>
</beans>
测试
package com.lihua.spring事务_声明式02.test;
import com.lihua.spring事务_声明式02.service.BankService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 15594
*/
public class Test {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-transaction2.xml");
//代理对象,里面织入了事务增强
BankService proxyBankService=(BankService) ac.getBean("bankService");
proxyBankService.transfer("李华", "小明", 200.0);
System.out.println("转账成功");
}
}
3、注解式
数据层(不变)
接口
package com.lihua.spring事务_注解式03.mapper;
/**
* 转账操作
* @author 15594
*/
public interface BankMapper {
/**
* 收款人余额增加余额操作
*
* @param name 收款人
* @param money 收款金额
* */
public void addMoney(String name, Double money);
/**
* 付款人余额减少余额操作
*
* @param name 付款人
* @param money 付款金额
* */
public void reduceMoney(String name, Double money);
}
实现类
package com.lihua.spring事务_注解式03.mapper.impl;
import com.lihua.spring事务_注解式03.mapper.BankMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
/**
*
* @author 15594
*/
@Repository("bankMapper")
public class BankMapperImpl implements BankMapper {
@Autowired
@Qualifier("jdbcTemplate")
private JdbcTemplate jdbcTemplate;
public void addMoney(String name, Double money) {
//模拟超时
// try {
// Thread.sleep(15000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
String sql="update bank_card set money= money+ ? where username=?";
jdbcTemplate.update(sql, money,name);
}
public void reduceMoney(String name, Double money) {
// try {
// Thread.sleep(15000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
String sql="update bank_card set money= money- ? where username=?";
jdbcTemplate.update(sql, money,name);
}
}
业务层(核心代码)
接口
package com.lihua.spring事务_注解式03.service;
/**
* @author 15594
*/
public interface BankService {
/**
* 转账业务
* @param payee 收款人
* @param payer 付款人
* @param money 转账金额
* */
void transfer(String payee, String payer, Double money);
}
实现类
package com.lihua.spring事务_注解式03.service.impl;
import com.lihua.spring事务_注解式03.mapper.BankMapper;
import com.lihua.spring事务_注解式03.service.BankService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* @author 15594
*/
@Service("bankService1")
//@Transactional(rollbackFor = Exception.class)
public class BankServiceImpl implements BankService {
@Autowired
@Qualifier("bankMapper")
private BankMapper bankMapper;
/**
* 注意:spring事务默认出现运行时异常,回滚事务。编译时异常不会回滚
* 所有我们想要回滚事务,可以制造运行时异常。
* */
@Transactional(
//事务隔离级别(读未提交,读已提交(解决脏读),可重复读(解决不可重复读,一般由修改造成不可重复读),串行(最高的隔离级别,解决幻读,一般由新增删除造成幻读))
isolation = Isolation.DEFAULT,
//超时时间,-1为使用底层事务系统的默认超时,如果不支持超时,则不使用。以秒为单位
timeout = 3,
//是否只读,有更新操作为:false
readOnly = false,
//指定回滚抛出的异常类型
rollbackFor = Exception.class
//事务传播方式
//,propagation = Propagation.REQUIRED
)
public void transfer(final String payee, final String payer, final Double money) {
//收钱
bankMapper.addMoney(payee,money);
//spring 事务默认出现运行时异常回滚事务
int i =1/0;
//付钱
bankMapper.reduceMoney(payer,money);
}
}
spring.xml配置(更改配置)
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--包扫面-->
<context:component-scan base-package="com.lihua.spring事务_注解式03"/>
<!--开启注解支持-->
<context:annotation-config></context:annotation-config>
<!--当然我们也可以根据外部的配置文件(key-value格式的文件,比如yml、properties)加载数据源-->
<context:property-placeholder location="classpath:db.properties"/>
<bean id = "datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
<!--配置事务管理器,需要一个数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!--jdbcTemplate模板类,需要一个数据源。注意:jdbcTemplate必须是事务管理器支持的-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="datasource"></property>
</bean>
<!--注解驱动,对添加@Transactional注解的Bean织入事务管理,peoxy-target-class属性如果为true,使用CGLib-->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
测试
package com.lihua.spring事务_注解式03.test;
import com.lihua.spring事务_注解式03.service.BankService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 15594
*/
public class Test {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-transaction3.xml");
//代理对象,里面织入了事务增强
BankService bankService = (BankService)ac.getBean("bankService1");
bankService.transfer("李华", "小明", 200.0);
System.out.println("成功");
}
}
4、ssm框架使用事务
使用与上面的注解式差不多(一样),
4.1、导入pom依赖(注意版本问题)
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<!--导入mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--end 导入mybatis-->
<!-- 数据库驱动jar -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- end 数据库驱动jar -->
<!-- druid数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<!-- end druid数据源 -->
<!--导入spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.8</version>
</dependency>
<!--end 导入spring-->
<!-- mybatis整合spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- end mybatis整合spring-->
<!--导入spring MVC 必要的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- end spring MVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
</dependencies>
4.2、spring配置
在整合ssm spring配置的基础上加上下面的配置即可
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注解支持-->
<tx:annotation-driven transaction-manager="transactionManager" />
4.3、通过注解使用事务
package com.lihua.ssm.service.impl;
import com.lihua.ssm.mapper.TransferMapper;
import com.lihua.ssm.pojo.BankCard;
import com.lihua.ssm.service.TransferService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @author 15594
*/
@Service
public class TransferServiceImpl implements TransferService {
@Autowired
private TransferMapper transferMapper;
@Transactional(isolation = Isolation.READ_COMMITTED,rollbackFor =Exception.class )
public boolean transfer(String payee, String payer, Double money) {
//收钱
transferMapper.addMoney(payee,money);
//spring 事务默认出现运行时异常回滚事务
int i =1/0;
//付钱
transferMapper.reduceMoney(payer,money);
System.out.println("转账成功");
return true;
}
public List<BankCard> queryBankCard() {
return transferMapper.queryBankCard();
}
}
5、spring boot使用事务
5.1、导入pom依赖
在Spring Boot中,当我们使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa依赖的时候,框 架会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager。所以我们不需要任何额外 配置就可以用@Transactional注解进行事务的使用。
5.2、使用
spring Boot 使用事务非常简单,首先使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可。
主启动类
package com.lihua.newideas;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
// 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven /> 使用cglib动态代理
@EnableTransactionManagement(proxyTargetClass = true)
@EnableGlobalMethodSecurity(securedEnabled = true , prePostEnabled = true) // 开启授权注解支持
public class NewIdeasApplication {
public static void main(String[] args) {
SpringApplication.run(NewIdeasApplication.class, args);
}
}
使用注解开始事务
//更改密码的业务逻辑
@Transactional
@Override
public String updateUserByUserName(String passWord, String userName, String code) {
MyUser myuser = queryUserByName(userName);
if(myuser==null){
return "没有这个用户";
}else {
if(myuser.getCode().equals(code)){
String encode = passwordEncoder.encode(passWord);
myUserMapper.updateUserByUserName(encode,userName,code);
//执行完数据层后抛出异常,开启事务后会回滚
int i =1/0;
return "修改成功";
}else {
return "密保错误";
}
}
}
5.3、手动提交回滚事务
spring boot事务回滚(出现运行时异常)与提交(没有异常)都是自动完成的我们也可以手动操作。
具体可以参考:
SpringBoot声明式事务的简单运用