下面的示例需要的jar包
<!--spring 核心--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.17.RELEASE</version> </dependency> <!--Servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <!--jdbc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.8.RELEASE</version> </dependency> <!--mysql 驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--junit 测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13-beta-2</version> </dependency> <!--测试--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.1.7.RELEASE</version> </dependency> <!--c3p0连接池--> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.4</version> </dependency> <!--dpcp 连接池--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.6.0</version> </dependency> <!--dpcp 连接池需要依赖--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.2</version> </dependency> <!--AOP:全自动配置代理--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
事物场景当中,抛出异常被catch之后,如果要回滚事务,一定要手动回滚
java异常包括checked exception
(检查异常) 和 unchecked exception
(非检查异常),其中未检查异常又叫RuntimeException
(运行时异常),对于运行时异常,java
编译器不要求对异常进行处理(捕获或者抛出),但是对于checked exception
异常,必须要在方法里面捕获或者继续抛出。
上面一句话的意思就很明显了,spring默认对于运行时的异常进行捕获的,如果你在方法中添加了try{}catch{}
捕获了异常,则spring
是不会对事务进行回滚的,因为他默认你已经对异常进行了回滚,如果想要事务回滚,则必须在catch
中继续抛出异常。
注意
这里抛出的异常需要是RuntimeException
的子类,如果需要设置spring
也识别其他的异常并进行回滚,则需要设置作用在该方法上的@Transactional
注解的rollbackFor
属性。例如:
@Transaction(rollbackFor = MyException.class)
这里如果在方法中抛出的异常是MyExcepiton
的子类,事务也会进行回滚。
事务回滚操作
SpringBoot提供了非常方便的事务操作,通过注解就可以实现事务的回滚,非常方便快捷,下面我就说一下如何进行事务操作。
@Transactional可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
如果我们需要捕获异常后,同时进行回滚,通过TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();进行手动回滚操作。
使用Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
设置回滚点,使用TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);回滚到savePoint
一、Spring 事务管理 API
1.1 三个顶级接口
PlatformTransactionManager:事务管理器接口(spring管理事务必须使用事务管理器),其主要用于完成事务的提交、回滚,及获取事务的状态信息。
TransactionDefinition :事务定义接口,定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作。
TransactionStatus :事务状态,spring用于记录当前事务的运行状态,例如是否有保存点,事务是否完成。spring底层根据状态进行相应的操作(该接口知道即可)
1.1.1 PlatformTransactionManager
两个实现类,事务管理器(必须配置)
DataSourceTransactionManager
:使用 JDBC 或 MyBatis 进行持久化数据时使用。HibernateTransactionManager
:使用 Hibernate 进行持久化数据时使用。
该接口定义了 3 个事务方法:
void commit(TransactionStatus status)
:事务的提交TransactionStatus getTransaction(TransactionDefinition definition)
:获取事务的状态void rollback(TranscationStatus status)
:事务的回滚
1.1.2 TransactionDefinition
TIMEOUT_DEFAULT:int 默认超时时间。默认值:1。使用款据库底层的时时间。 getPropagationBehavior0:int 传播行为 getlsolationLevel0:int 隔离级别 getTimeout0:int 获得超时时间 isReadOnly0:boolean 是否只读(增删;读写;查询;只读) getName0:String 配置务详情名称。一般方法名称。如:save、add*等
1.1.3 TransactionStatus(了解)
isNewTransaction0:boolean 是否是新的事务 hasSavepoint0:boolean 是否有保存点 setRollbackOnly0:void 设置回滚 isRollbackOnly0:boolean 是否回滚 flush0:void 刷新 isCompleted0:boolean 是否完成
1.2 事务的七种传播行为
参考:https://blog.csdn.net/weixin_39625809/article/details/80707695
所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如,A 事务中的方法 a()
调用 B 事务中的方法 b()
,在调用执行期间事务的维护情况,就称为事务传播行为。事务传播行为是加在方法上的。
不同业务之间共享事务
- REQUIRED:指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。
- SUPPORTS:指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
- MANDATORY:指定的方法必须在当前事务内执行,若当前没有事务,则直接抛出异常。
- REQUIRES_NEW:总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
- NOT_SUPPORTED:指定的方法不能在事务环境中执行,若当前存在事务,就将当前事务挂起。指定方法以非事务执行,如果当前不存在事务,指定方法还是以非事务执行。
- NEVER:指定的方法不能在事务环境下执行,若当前存在事务,就直接抛出异常。如果当前没有事务,指定方法以非事务执行
- NESTED:指定的方法必须在事务内执行。若当前存在事务,则在嵌套事务内执行;若当前没有事务,则创建一个新事务。
如何指定传播类型:可以在注解或者XML中指定传播类型, 如 “@Transactional(Propagation=xxx)”
spring管理事务回滚的时候,自己不可以在代码中try 。。。catch。。。
二、综合示例
使用技术 JdbcTemplate、 c3p0(连接池) 、
重要的jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.8.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>
2.1 搭建环境
创建数据表
CREATE TABLE acount( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(10), money INT ); INSERT INTO acount(username,money) VALUES("渣渣辉",100); INSERT INTO acount(username,money) VALUES("古天乐",100);
2.2手动管理事务(了解)
spring 底层 通过TransactionTemplate 事务模板来进行操作
使用 TransactionTemplate,需要手动给他注入事务管理器,给事务管理器注入连接池(datasource) [事务管理器就行一个没有车的马达,而连接池就是马达,而马达可以更换性能更高的马达]
UserAcountDaoImpl
public class UserAcountDaoImpl extends JdbcDaoSupport{ //用户账号转入 public void in(String innerUsername,int money){ this.getJdbcTemplate().update("UPDATE acount SET money=money+? WHERE username=?;",money,innerUsername); } //用户账号转出 public void out(String outerUsername,int money){ this.getJdbcTemplate().update("UPDATE acount SET money=money-? WHERE username=?;",money,outerUsername); } }
UserAcountServiceImpl
public class UserAcountServiceImpl { @Autowired private UserAcountDaoImpl userAcountDao; private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } //转账(核心) public void transfer(final String outerUsername,final String innerUsername,final int money){ transactionTemplate.execute(new TransactionCallbackWithoutResult(){ protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { userAcountDao.out(outerUsername,money); //测试报错 int a=1/0; userAcountDao.in(innerUsername,money); } }); } }
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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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 https://www.springframework.org/schema/aop/spring-aop.xsd"> <context:annotation-config></context:annotation-config> <context:component-scan base-package="com.test"></context:component-scan> <!-- 加载配置文件--> <context:property-placeholder location="jdbcinfo.properties"></context:property-placeholder> <!-- 如果配置文件没有放在资源目录,下面这种写法就可以找到,classpath:表示定位到src目录下--> <!-- <context:property-placeholder location="classpath:com.test.xx.properties"></context:property-placeholder>--> <bean id="basicDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="basicDataSource"></property> </bean> <bean id="userDaoImpl" class="com.test.UserAcountDaoImpl"> <property name="jdbcTemplate" ref="JdbcTemplate"></property> </bean> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="basicDataSource"></property> </bean> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="dataSourceTransactionManager"></property> </bean> <bean id="userAcountServiceImpl" class="com.test.UserAcountServiceImpl"> <property name="transactionTemplate" ref="transactionTemplate"></property> </bean> </beans>
测试
public class DemoTest { public static void main(String[] args) { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml"); UserAcountServiceImpl userAcountServiceImpl = (UserAcountServiceImpl) classPathXmlApplicationContext.getBean("userAcountServiceImpl"); userAcountServiceImpl.transfer("渣渣辉","古天乐",10); } }
2.3 工厂Bean生成代理,管理事务(了解)
xml 不需要配置 TransactionTemplate,任然需要配置事务管理器,连接池,加上一个 TransactionProxyFactoryBean 工厂Bean
UserAcountDao
public class UserAcountDaoImpl extends JdbcDaoSupport{ //用户账号转入 public void in(String innerUsername,int money){ this.getJdbcTemplate().update("UPDATE acount SET money=money+? WHERE username=?;",money,innerUsername); } //用户账号转出 public void out(String outerUsername,int money){ this.getJdbcTemplate().update("UPDATE acount SET money=money-? WHERE username=?;",money,outerUsername); } }
UserAcountService(需要有接口)
public interface UserAcountService { public void transfer(final String outerUsername,final String innerUsername,final int money); }
UserAcountServiceImpl
public class UserAcountServiceImpl implements UserAcountService{ @Autowired private UserAcountDaoImpl userAcountDao; //转账 public void transfer(final String outerUsername,final String innerUsername,final int money){ userAcountDao.out(outerUsername,money); //测试报错 int a=1/0; userAcountDao.in(innerUsername,money); } }
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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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 https://www.springframework.org/schema/aop/spring-aop.xsd"> <context:annotation-config></context:annotation-config> <context:component-scan base-package="com.test"></context:component-scan> <!-- 加载配置文件--> <context:property-placeholder location="jdbcinfo.properties"></context:property-placeholder> <!-- 如果配置文件没有放在资源目录,下面这种写法就可以找到,classpath:表示定位到src目录下--> <!-- <context:property-placeholder location="classpath:com.test.xx.properties"></context:property-placeholder>--> <bean id="basicDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="basicDataSource"></property> </bean> <bean id="userDaoImpl" class="com.test.UserAcountDaoImpl"> <property name="jdbcTemplate" ref="JdbcTemplate"></property> </bean> <!-- 配置事务管理器,注意Mybaits和Hibernate使用的不一样--> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="basicDataSource"></property> </bean> <bean id="userAcountServiceImpl" class="com.test.UserAcountServiceImpl"></bean> <!-- 配置代理类--> <bean id="transactionProxyFactoryBean" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="proxyInterfaces" value="com.test.UserAcountService"></property><!--如果ServiceImpl没有接口,可以不写--> <property name="target" ref="userAcountServiceImpl"></property> <property name="transactionManager" ref="dataSourceTransactionManager"></property> <!-- 事务属性--> <property name="transactionAttributes"> <!-- prop.key:确定哪些方法使用当前事务配置 prop.text:用于配置事务详情 格式 : PROPAGATION,ISOLATION,readOnly,-Exception,+Exception 传播行为 隔离级别 是否只读 异常回滚 异常提交 --> <props> <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-Exception</prop><!--配置了默认的传播行为和默认隔离级别--> </props> </property> </bean> </beans>
测试
public class DemoTest { public static void main(String[] args) { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml"); UserAcountService userAcountServiceImpl = (UserAcountService) classPathXmlApplicationContext.getBean("transactionProxyFactoryBean"); userAcountServiceImpl.transfer("渣渣辉","古天乐",10); } }
2.4 AOP配置基于xml(掌握)
在spring中配置aop自动生成代理,进行事务管理
1、配置事务管理器
2、配置事务详情
3、配置aop
UserServiceDaoImpl
public class UserAcountDaoImpl extends JdbcDaoSupport{ //用户账号转入 public void in(String innerUsername,int money){ this.getJdbcTemplate().update("UPDATE acount SET money=money+? WHERE username=?;",money,innerUsername); } //用户账号转出 public void out(String outerUsername,int money){ this.getJdbcTemplate().update("UPDATE acount SET money=money-? WHERE username=?;",money,outerUsername); } }
UserService
public interface UserAcountService { public void transfer(final String outerUsername,final String innerUsername,final int money); }
UserServiceImpl
@Service("userAcountServiceImpl") public class UserAcountServiceImpl implements UserAcountService{ @Autowired private UserAcountDaoImpl userAcountDao; //转账 public void transfer(final String outerUsername,final String innerUsername,final int money){ userAcountDao.out(outerUsername,money); //测试报错 int a=1/0; userAcountDao.in(innerUsername,money); } }
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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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 https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <context:annotation-config></context:annotation-config> <context:component-scan base-package="com.test"></context:component-scan> <!-- 加载配置文件--> <context:property-placeholder location="jdbcinfo.properties"></context:property-placeholder> <!-- 如果配置文件没有放在资源目录,下面这种写法就可以找到,classpath:表示定位到src目录下--> <!-- <context:property-placeholder location="classpath:com.test.xx.properties"></context:property-placeholder>--> <bean id="basicDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="basicDataSource"></property> </bean> <bean id="userDaoImpl" class="com.test.UserAcountDaoImpl"> <property name="jdbcTemplate" ref="JdbcTemplate"></property> </bean> <!-- 配置事务管理器--> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="basicDataSource"></property> </bean> <!-- 事务通知(详细|定义) <tx:attributes> :配置事务详情 <tx:method:详情的具体配置 name:切入点的某一个方法 propagation:传播行为 isolation:隔离级别 read-only:只读(用于查询语句) --> <tx:advice id="txadvice" transaction-manager="dataSourceTransactionManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/> </tx:attributes> </tx:advice> <!-- 配置AOP--> <aop:config> <aop:advisor advice-ref="txadvice" pointcut="execution(* com.test.UserAcountServiceImpl.*(..))"></aop:advisor> </aop:config> </beans>
测试
public class DemoTest { public static void main(String[] args) { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml"); UserAcountService userAcountServiceImpl = (UserAcountService) classPathXmlApplicationContext.getBean("userAcountServiceImpl"); userAcountServiceImpl.transfer("渣渣辉","古天乐",10); } }
2.4 AOP配置基于注解(掌握)
1、配置事务管理器,将事务管理器交给spring
2、在目标类或目标方法添加注解即可,@Transactional
UserAcountDaoImpl
public class UserAcountDaoImpl extends JdbcDaoSupport{ //用户账号转入 public void in(String innerUsername,int money){ this.getJdbcTemplate().update("UPDATE acount SET money=money+? WHERE username=?;",money,innerUsername); } //用户账号转出 public void out(String outerUsername,int money){ this.getJdbcTemplate().update("UPDATE acount SET money=money-? WHERE username=?;",money,outerUsername); } }
UserAcountService
public interface UserAcountService { public void transfer(final String outerUsername,final String innerUsername,final int money); }
UserAcountServiceImpl(添加注解@Transactional)
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false) //如果注解加载类上,表示该类的所有的方法都用事务,如果加载方法上,表示这一个方法有事务[readOnly=true:表示只读,只可以加在查询上,可以不写] @Service("userAcountServiceImpl") public class UserAcountServiceImpl implements UserAcountService{ @Autowired private UserAcountDaoImpl userAcountDao; //转账 public void transfer(final String outerUsername,final String innerUsername,final int money){ userAcountDao.out(outerUsername,money); //测试报错 // int a=1/0; userAcountDao.in(innerUsername,money); } }
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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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 https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <context:annotation-config></context:annotation-config> <context:component-scan base-package="com.test"></context:component-scan> <!-- 加载配置文件--> <context:property-placeholder location="jdbcinfo.properties"></context:property-placeholder> <!-- 如果配置文件没有放在资源目录,下面这种写法就可以找到,classpath:表示定位到src目录下--> <!-- <context:property-placeholder location="classpath:com.test.xx.properties"></context:property-placeholder>--> <bean id="basicDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">--> <!-- <property name="dataSource" ref="basicDataSource"></property>--> <!-- </bean>--> <!-- <bean id="userDaoImpl" class="com.test.UserAcountDaoImpl">--> <!-- <property name="jdbcTemplate" ref="JdbcTemplate"></property>--> <!-- </bean>--> <!-- 下面这 相当上面的代码,setDataSource会自动SetJdgcTemplate,并将dataSource传递进去,可以自己查看源码--> <bean id="xx" class="com.test.UserAcountDaoImpl"> <property name="dataSource" ref="basicDataSource"></property> </bean> <!-- 配置事务管理器--> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="basicDataSource"></property> </bean> <!-- 将事务管理器交给spring proxy-target-class=true :表示底层强制使用cglib代理 --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager" proxy-target-class="true"></tx:annotation-driven> </beans>
测试
public class DemoTest { public static void main(String[] args) { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml"); UserAcountService userAcountServiceImpl = (UserAcountService) classPathXmlApplicationContext.getBean("userAcountServiceImpl"); userAcountServiceImpl.transfer("渣渣辉","古天乐",10); } }
三、补充(可以忽略,基本和上面一样,不全)
3.1 使用 AspectJ 的 AOP 配置管理事务(即XML配置)
AspectJ 主要是使用 XML 配置顾问方式自动为每个符合切入点表达式的类生成事务代理。创建测试操作步骤如下:
创建一个名为 aspectj-aop
项目,pom.xml
文件如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>hello-spring-transaction</groupId> <artifactId>aspectj-aop</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <!-- 环境配置 --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <!-- 统一的依赖管理 --> <log4j.version>1.2.17</log4j.version> <slf4j.version>1.7.25</slf4j.version> <spring.version>4.3.17.RELEASE</spring.version> <alibaba-druid.version>1.1.6</alibaba-druid.version> <mysql.version>5.1.46</mysql.version> <mybatis.version>3.2.8</mybatis.version> <mybaits-spring.version>1.3.1</mybaits-spring.version> <junit.version>4.12</junit.version> <lombok.version>1.16.18</lombok.version> </properties> <dependencies> <!-- Test Begin --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <!-- Test Begin --> <!-- Spring Begin --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring End --> <!-- Log Begin --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <!-- Log End --> <!-- Database Begin --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${alibaba-druid.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybaits-spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!-- Database End --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> </dependencies> </project>
主要是增加了 org.springframework:spring-aspects
依赖
配置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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:annotation-config/> <context:component-scan base-package="com.hello.spring.transaction.aspectsj.aop"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> //选择MyBatis,则使用DataSourceTransactionManager <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置事务通知 --> <tx:advice id="myAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED"/> //表示以save开头的数据库操作,使用REQUIRED事务,可以自己根据需求增加 <tx:method name="get*" propagation="NEVER"/> //表示以get开头的数据库操作,不使用任何事务 </tx:attributes> </tx:advice> <!-- 配置顾问和切入点 --> <aop:config> <aop:pointcut id="myPointcut" expression="execution(* com.hello.spring.transaction.aspectsj.aop.service.*.*(..))" /> //表示在service目录下的所有的类的所有的方法 <aop:advisor advice-ref="myAdvice" pointcut-ref="myPointcut" /> </aop:config> </beans>
- 使用 ..,例如com.zy..service
3.2 使用 Spring 注解管理事务
实际开发里用的较多,开启事务注解驱动
<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:annotation-config/> <context:component-scan base-package="com.hello.spring.transaction.aspectsj.aop"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> //选择MyBatis,则使用DataSourceTransactionManager <property name="dataSource" ref="dataSource"/> </bean> <!-- 开启事务注解驱动 --> <tx:annotation-driven transaction-manager="transactionManager" /> </beans>
@Transactional
注解简介
@Transactional
的所有可选属性:
属性名 | 说明 |
---|---|
name | 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。 |
propagation | 事务的传播行为,默认值为 REQUIRED。 |
isolation | 事务的隔离度,默认值采用 DEFAULT。 |
timeout | 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。 |
read-only | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 |
rollback-for | 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。 |
no-rollback- for | 当抛出 no-rollback-for 指定的异常类型,不会进行事务回滚。 |
需要注意的是,@Transactional
若用在方法上,只能用于 public
方法上。对于其他非 public
方法,如果加上了注解 @Transactional
,虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public
方法上的 @Transaction
注解。
若 @Transaction
注解在类上,则表示该类上所有的方法均将在执行时织入事务。
使用 @Transaction
注解
使用起来很简单,只需要在需要增加事务的业务类上增加 @Transaction
注解即可
@Transactional @Service(value = "tbContentCategoryService") public class TbContentCategoryServiceImpl implements TbContentCategoryService { 。。。。。 }
实际业务中:
spring Boot事务
使用@Transaction
注解,没有任何配置