可优先参考:@Transactional Spring 事务的深入学习与使用【两万字】
1. 编程式事务控制
@Autowired
private RoleMapper roleMapper;
@Autowired
private PlatformTransactionManager platformTransactionManager;
@GetMapping("manualTransaction")
public Object manualTransaction() {
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
// 事务隔离级别
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
// 事务传播行为
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 是否只读事务
transactionDefinition.setReadOnly(false);
// 事务超时时间
transactionDefinition.setTimeout(-1);
TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
RoleEntity roleEntity = new RoleEntity();
roleEntity.setName("role001");
roleEntity.setIsDel(1);
roleEntity.setMerchantCode("1");
// spring事务接管mybatis事务的过程
// SqlSessionFactoryBean -> SpringManagedTransactionFactory -> SpringManagedTransaction -> DataSourceUtils.getConnection(this.dataSource)
roleMapper.insert(roleEntity);
log.info("编程式事务: roleEntity->{}, {}", roleEntity);
// if (1 > 0) {
// platformTransactionManager.commit(transactionStatus); // 提交事务
// return roleEntity;
// }
platformTransactionManager.rollback(transactionStatus); // 回滚事务
return roleEntity;
}
@Autowired
private TransactionTemplate transactionTemplate;
@GetMapping("manualTransaction2")
public Object manualTransaction2() {
// TransactionTemplate继承自DefaultTransactionDefinition(实现了TransactionDefinition接口)
// (其实就是上一种的简化版本)
RoleEntity roleEntity = transactionTemplate.execute(new TransactionCallback<RoleEntity>() {
@Override
public RoleEntity doInTransaction(TransactionStatus status) {
RoleEntity roleEntity = new RoleEntity();
roleEntity.setName("role001");
roleEntity.setIsDel(1);
roleEntity.setMerchantCode("1");
roleMapper.insert(roleEntity);
log.info("编程式事务: roleEntity->{}, {}", roleEntity);
// log.info("{}",status.isRollbackOnly());
// // status.setRollbackOnly(); // 1. 设置rollbackOnly为true, 会回滚事务
// log.info("{}",status.isRollbackOnly());
if (1 > 0) {
// throw new RuntimeException(); // 2. 抛出Runtime异常, 会回滚事务
}
return roleEntity;
}
});
log.info("执行结果: roleEntity->{}, {}", roleEntity, roleEntity.hashCode());
return roleEntity;
}
以下参考:Spring 编程式事务实例
1、通过PlatformTransactionManager控制事务
package com.tx;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.util.List;
import java.util.Map;
public class TestTransaction {
public static void main(String[] args) {
//1、定义数据源
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/work_db?characterEncoding=UTF-8&serverTimezone=UTC");
dataSource.setInitialSize(10);
dataSource.setUsername("root");
dataSource.setPassword("root");
//2、定义JdbcTemplate,执行数据库操作语句
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//3、定义【事务管理器】,并指定数据源
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
//4、定义【事务属性】,TransactionDefinition,它可用来配置事务的属性信息,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
//5.【开启事务】,返回事务状态(TransactionStatus)对象
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
//6.执行业务操作
try {
List<Map<String, Object>> list = jdbcTemplate.queryForList("SELECT * from b_user");
System.out.println("插入数据前:" + list);
jdbcTemplate.update("insert into b_user (name) values (?)", "李清华最聪明2");
jdbcTemplate.update("insert into b_user (name) values (?)", "张北大最智慧2");
//制造异常,触发回滚
int num = 1 / 0;
//7.【提交事务】,注意:事务的提交放在try最后一行
transactionManager.commit(transactionStatus);
} catch (Exception e) {
//8.【回滚事务】,捕获异常回滚事务
transactionManager.rollback(transactionStatus);
}
List<Map<String, Object>> list = jdbcTemplate.queryForList("SELECT * from b_user");
System.out.println("插入数据后:" + list);
}
}
2、通过TransactionTemplate控制事务
package com.tx;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.lang.Nullable;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.function.Consumer;
public class TestTransaction2 {
public static void main(String[] args) {
//定义数据源
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/work_db?characterEncoding=UTF-8&serverTimezone=UTC");
dataSource.setInitialSize(10);
dataSource.setPassword("root");
dataSource.setUsername("root");
//定义JdbcTemplate
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//1.定义事务管理器
PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
//2.定义事务属性
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setTimeout(10);//如:设置超时时间10s
//3.创建TransactionTemplate对象
TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager, transactionDefinition);
//无返回值的,需传递一个Consumer对象,在accept方法中做业务操作
transactionTemplate.executeWithoutResult(new Consumer<TransactionStatus>() {
@Override
public void accept(TransactionStatus transactionStatus) {
jdbcTemplate.update("insert into b_user (name) values (?)", "张三无返回");
}
});
//有返回值的,需要传递一个TransactionCallback对象,在doInTransaction方法中做业务操作
Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() {
@Nullable
@Override
public Integer doInTransaction(TransactionStatus status) {
int result = jdbcTemplate.update("insert into b_user (name) values (?)", "张三有返回");
return result;
}
});
System.out.println("执行返回数据:" + result);
System.out.println("最终查询结果:" + jdbcTemplate.queryForList("SELECT * from b_user"));
}
}
2. 声明式事务控制
1、基于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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
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.alibaba.com/schema/stat http://www.alibaba.com/schema/stat.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启Spring IoC组件注解包扫描,方便操作-->
<context:component-scan base-package="com.spring.tx.xml"/>
<!--druid数据源连接池-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url"
value="jdbc:mysql://47.94.229.245:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--jdbcTemplate 使用Spring JDBC模板类来操作数据库-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--配置一个druidDataSource数据源-->
<constructor-arg name="dataSource" ref="druidDataSource"/>
</bean>
<!--DataSourceTransactionManager事务管理器 用来管理数据库事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--配置一个druidDataSource数据源-->
<constructor-arg name="dataSource" ref="druidDataSource"/>
</bean>
<!--配置一个事务的AOP通知,通过transaction-manager属性关联一个事务管理器
默认查找id/name为transactionManager的事务管理器,因此如果事务管理器beanName就是transactionManager,那么该属性可以省略 -->
<!--该标签可以配置多个,可以使用不同的事务管理器-->
<!--此标签可以定义定义许多方法的事务语义(其中事务语义包括传播设置、隔离级别、回滚规则等)-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--一个标签集,内部可以配置多个<tx:method/>标签 -->
<tx:attributes>
<!--配置某些方法的事务属性,这些方法是从aop:pointcut切入点表达式匹配到的方法中进一步筛选出来的-->
<!-- 该标签可配置的属性
name: 表示匹配的方法名,可以使用通配符*
read-only:是否是只读事务。默认false,不是。
isolation:指定事务的隔离级别。默认值为DEFAULT,即对应ISOLATION_DEFAULT
propagation:指定事务的传播行为。默认值为REQUIRED,即对应PROPAGATION_REQUIRED
timeout:指定超时时间。默认值为:-1。永不超时。
rollback-for:指定将触发回滚的一个或多个异常,使用","分隔。如不指定则使用默认策略。
no-rollback-for:指定不会触发回滚的一个或多个异常,使用","分隔。如不指定则使用默认策略。
-->
<!--以"get"开始的所有方法都配置只读属性-->
<tx:method name="get*" read-only="true" timeout="30"/>
<!-- 其他剩余方法使用默认事务属性配置-->
<tx:method name="*" timeout="30"/>
</tx:attributes>
</tx:advice>
<!-- AOP配置,确保上述事务通知正常运行-->
<aop:config>
<!--配置aop切入点表达式 该表达式用于确定到底有哪些方法需要进行事务管理-->
<!--这里,我们配置TxStudyServiceImpl的所有方法均需要被事务管理-->
<aop:pointcut id="studyServicePt" expression="execution(* com.spring.tx.xml.TxStudyServiceImpl.*(..))"/>
<!--配置aop通知器 使用advice-ref引用上面定义的事务通知(通过指向它的id) 通过pointcut-ref引入上面定义的aop切入点表达式(通过指向它的id)
这样,通过pointcut-ref引入的切入点表达式匹配的方法就可以应用于通过advice-ref引入的事务通知机制以及各种属性
-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="studyServicePt"/>
</aop:config>
</beans>
甚至在声明式的事务管理配置中,也可以以编程方式指示所需的回滚。虽然简单,但此过程非常具有侵入性。
private void test() throws IOException {
try {
// 业务代码
} catch (Exception ex) {
// 捕获异常之后,以编程方式手动触发回滚
// currentTransactionStatus方法:获取当前事务,如果开启了事务,那么会返回事务,如果没有事务,那么会抛出异常!
// setRollbackOnly方法:用于设该事务的唯一结果就回滚,用来代替抛出异常
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
2、基于xml声明式注解事务控制
```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:tx="http://www.springframework.org/schema/tx"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启Spring IoC组件注解包扫描,方便操作-->
<context:component-scan base-package="com.spring.tx.ann"/>
<!--druid数据源连接池-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url"
value="jdbc:mysql://47.94.229.245:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--jdbcTemplate 使用Spring JDBC模板类来操作数据库-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--配置一个druidDataSource数据源-->
<constructor-arg name="dataSource" ref="druidDataSource"/>
</bean>
<!--DataSourceTransactionManager事务管理器 用来管理数据库事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--配置一个druidDataSource数据源-->
<constructor-arg name="dataSource" ref="druidDataSource"/>
<qualifier value="11"/>
</bean>
<!-- 使用< tx:annotation-driven />标签开启事务注解的支持。此时我们不必再配置< tx:advice/>和< aop:config/>,可以直接在Spring管理的bean中添加事务注解
1. transaction-manager属性,用于指定驱动事务的事务管理器的 beanName。此属性不是必需的,默认值为transactionManager,只有在所需的事务管理器的 beanName不是"transactionManager"时才需要显式指定。
2. proxy-target-class属性,用于指定控制为使用@Transactional注解注解的类创建的事务代理类型。如果proxy-target-class属性设置为true,则创建基于类的代理。如果proxy-target-class为false或者省略了该属性,则会默认创建基于标准JDK接口的代理。
3. order属性,使用@Transactional注解的bean,当多个通知在特定连接点执行时,控制事务通知器的执行顺序。
-->
<!--启用基于注解的事务行为配置,通过transaction-manager属性关联一个事务管理器 -->
<!--默认值为transactionManager,因此如果事务管理器beanName就是transactionManager,那么该属性可以省略-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
3、纯注解的声明式事务
/*
@EnableTransactionManagement注解,用于替代< tx:annotation-driven/>标签来开启事务注解驱动
*/
@ComponentScan
@Configuration
@EnableTransactionManagement
public class PureAnnStart {
/**
* 配置Druid数据源
*/
@Bean
public DruidDataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
//为了方便,直接硬编码了,我们可以通过@Value引入外部配置,
//如果使用Spring boot就更简单了,直接使用@ConfigurationProperties引入外部配置
//简单的配置数据库连接信息,其他连接池信息采用默认配置
druidDataSource.setUrl("jdbc:mysql://47.94.229.245:3306/test?useSSL=false&allowPublicKeyRetrieval=true");
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUsername("root");
druidDataSource.setPassword("123456");
return druidDataSource;
}
/**
* 配置JdbcTemplate
* 直接使用spring-jdbc来操作某一个数据库,不使用其他外部数据库框架
*/
@Bean
public JdbcTemplate jdbcTemplate() {
//传入一个数据源
return new JdbcTemplate(druidDataSource());
}
/**
* 配置DataSourceTransactionManager
* 用于管理某一个数据库的事务
*/
@Bean
public DataSourceTransactionManager transactionManager() {
//传入一个数据源
return new DataSourceTransactionManager(druidDataSource());
}
}