事务是指数据库操作的一个执行单元,它是由一组有序的 SQL 语句组成的。
在执行过程中,要么都执行成功,要么都执行失败。如果其中一条 SQL 语句执行失败,整个事务就会被回滚,回到事务开始前的状态。
spring 事务管理器是基于 AOP 技术实现的,它可以管理一组相关的事务操作。在同一个事务中,多个数据库操作可以被视为一个整体,如果其中任何一个操作失败,整个事务都将被回滚。
spring 事务的传播行为是指,当一个事务方法调用了其他方法时,这些方法应该如何处理事务。spring 事务一般采用以下传播行为:
Propagation.REQUIRED(默认):如果当前存在事务,则加入该事务;否则创建一个新事务。
Propagation.SUPPORTS:如果当前存在事务,则加入该事务;否则以非事务方式执行。
Propagation.MANDATORY:必须在当前事务内运行;否则抛出异常。
Propagation.REQUIRES_NEW:创建一个新事务,如果当前存在事务,则挂起当前事务。
Propagation.NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则挂起当前事务。
Propagation.NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
Propagation.NESTED:如果当前存在事务,则在嵌套事务内运行;否则创建一个新事务。
spring 事务的隔离级别是指在并发环境下,不同的事务之间需要隔离的程度。spring 支持以下五种事务隔离级别:
Isolation.DEFAULT(默认):使用数据库默认的隔离级别。
Isolation.READ_UNCOMMITTED:允许读取未提交的数据。
Isolation.READ_COMMITTED:只能读取已提交的数据。
Isolation.REPEATABLE_READ:保证同一事务中多次读取同一数据时,得到的结果是一致的。
Isolation.SERIALIZABLE:最高的事务隔离级别,保证事务之间的完全隔离。
spring 事务在什么情况下会回滚?通常情况下,spring 事务遵循以下两个原则:
默认情况下,当抛出 RuntimeException 和其子类异常时,spring 事务会进行回滚。
如果方法中主动抛出了 Exception 异常或者其他检查异常,则不会进行回滚。
可以通过 @Transactional 的 noRollbackFor 属性和 rollbackFor 属性来指定不回滚的异常和回滚的异常。
spring 事务的实现方式通常有两种:基于 XML 配置和基于注解配置。
在 XML 配置文件中,我们可以通过
元素来定义事务的属性和传播行为,然后使用
元素将其与目标类或目标方法进行织入。
举例来说,以下是一个基于 XML 配置的事务声明:
<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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example.demo"/>
<context:annotation-config/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mya_test"/>
<property name="user" value="root"/>
<property name="password" value=""/>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transferAccounts" isolation="READ_COMMITTED" propagation="REQUIRED" read-only="false"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="servicePointCut"
expression="execution(public void com.example.demo.service.UserServiceImplXML.transferAccounts(..))"/>
<aop:advisor pointcut-ref="servicePointCut" advice-ref="txAdvice"/>
aop:config>
beans>
<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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example.demo"/>
<context:annotation-config/>
<aop:aspectj-autoproxy/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mya_test"/>
<property name="user" value="root"/>
<property name="password" value=""/>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
bean>
<bean name="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
beans>
import com.example.demo.dao.UserDao;
import com.example.demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED, readOnly = true)
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED, readOnly = false)
/**
* isolation:隔离级别
* propagation:传播行为
* readOnly:是否只读,默认false
*/
public boolean transferAccounts(User user1, User user2, double money) {
// 1减钱
user1.setMoney(user1.getMoney() - money);
userDao.updateUser(user1, user1.getId());
// System.out.println(10/0);
// 2加钱
user2.setMoney(user2.getMoney() + money);
userDao.updateUser(user2, user2.getId());
return true;
}
}
// 定义一个事务管理类,用于开启、提交或回滚事务
public class TransactionManager {
/**
* 开始事务
*/
public void begin() {
System.out.println("开始事务...");
}
/**
* 提交事务
*/
public void commit() {
System.out.println("提交事务...");
}
/**
* 回滚事务
*/
public void rollback() {
System.out.println("回滚事务...");
}
}
public interface Service {
void addUser();
}
public class UserServiceImpl implements Service {
@Override
public void addUser() {
System.out.println("添加用户...");
}
}
// 定义一个切面类,用于添加事务管理功能
public class TransactionAspect {
private TransactionManager transactionManager;
/**
* 设置事务管理器
*/
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* 开始事务
*/
public void beginTransaction() {
transactionManager.begin();
}
/**
* 提交事务
*/
public void commitTransaction() {
transactionManager.commit();
}
/**
* 回滚事务
*/
public void rollbackTransaction() {
transactionManager.rollback();
}
/**
* 环绕通知,在目标方法周围织入通知
*/
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
beginTransaction();
joinPoint.proceed(); // 执行目标方法
commitTransaction();
} catch (Throwable e) {
rollbackTransaction();
throw e;
}
}
}
注解说明:
@Around
:表示在目标方法周围织入通知。@Pointcut
:表示定义一个切点,通过表达式匹配需要拦截的目标方法。
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="transactionManager" class="com.example.TransactionManager"/>
<bean id="userService" class="com.example.UserServiceImpl"/>
<bean id="transactionAspect" class="com.example.TransactionAspect">
<property name="transactionManager" ref="transactionManager"/>
bean>
<aop:config>
<aop:aspect ref="transactionAspect">
<aop:pointcut id="serviceMethod" expression="execution(* com.example.Service.*(..))"/>
<aop:around pointcut-ref="serviceMethod" method="around"/>
aop:aspect>
aop:config>
beans>
注解说明:
:将 TransactionManager
、UserServiceImpl
和 TransactionAspect
配置为 Spring 容器中的 Bean。
:配置 AOP。
:表示定义一个切面。
:定义切点,通过表达式匹配需要拦截的目标方法。
:定义环绕通知,在目标方法周围织入通知,注意 method
属性要指向切面类中的方法名。