解决@Transactional注解事务不回滚不起作用的问题的详细攻略如下:
问题描述
在进行数据库操作时,我们通常会使用@Transactional注解来保证事务的原子性,但在使用过程中可能会出现事务不回滚不起作用的问题,导致数据一旦出现异常就无法恢复。这种情况通常是因为注解失效或者配置不正确导致的。
解决方案
1. 配置文件中开启事务管理器
我们可以在配置文件中开启事务管理器以确保每次数据库操作都处于一个事务当中,即将以下配置加入到application.yml或application.properties文件中:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
show_sql: true
format_sql: true
database: mysql
junit:
jupiter:
enabled: true
main:
allow-bean-definition-overriding: true
cache:
caffeine:
spec: maximumSize=500, expireAfterAccess=600s
session:
redis:
flush-mode: on_save
namespace: session:
key-prefix: session:
repository-type: spring
redis-template: redisTemplate
redis_namespace: spring:
cleanup-cron: 0 * * * * *
data:
redis:
host: 127.0.0.1
port: 6379
database: 0
lettuce:
pool:
max-active: 20
max-wait: -1
max-idle: 20
min-idle: 1
redis:
host: 127.0.0.1
port: 6379
mybatis:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.github.chenweitao.model
plugins:
- com.github.pagehelper.PageInterceptor
# 开启事务管理器
spring.transaction.default-timeout=60
spring.jpa.properties.hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JBossTransactionManagerLookup
2. 使用数据源代理
如果配置文件中配置有问题导致事务管理器无法正常使用,我们可以使用数据源代理来实现事务的回滚。我们需要用到Spring提供的DataSourceTransactionManager和TransactionInterceptor类,具体步骤如下:
- 在启动类中添加注解@EnableTransactionManagement开启事务管理。
- 添加事务管理器的bean。
@Configuration
public class TransactionConfig {
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource){
return new DataSourceTransactionManager(dynamicDataSource);
}
}
- 定义切面并添加事务拦截。
@Aspect
@Component
public class TransactionAspect {
private final Logger logger = LoggerFactory.getLogger(TransactionAspect.class);
private static final String EXPRESSION = "execution(* com.github.chenweitao.mapper.*.*(..))";
@Autowired
private DataSourceTransactionManager transactionManager;
@Around(EXPRESSION)
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Transactional transactional = method.getAnnotation(Transactional.class);
if (transactional == null) {
return point.proceed();
}
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
Object result;
try {
result = point.proceed();
transactionManager.commit(status);
logger.info("Transaction Commit");
} catch (Throwable e) {
transactionManager.rollback(status);
logger.error("Transaction Rollback");
throw e;
}
return result;
}
}
3. 示例说明
现在我们来通过两个示例来说明解决@Transactional注解事务不回滚不起作用的问题。
示例一: 抛出Unchecked Exception
在该例子中,我们假设数据库中已经存在一个数据,我们在新增数据时抛出一个Unchecked Exception,并期望两条insert语句都不会提交到数据库。
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
@Transactional
public boolean addUsers() {
User user1 = new User("chenweitao", 18, 99.99);
userMapper.insert(user1);
User user2 = new User("xiaoxia", 20, 100.0);
int n = 10/0;// 抛出一个Unchecked Exception
userMapper.insert(user2);
return true;
}
}
此时,因为我们在ServiceImpl类上添加了@Transactional注解,期望所有操作都属于同一个事务,但是实际上抛出了一个Exception,事务并未回滚,两条insert语句都提交到了数据库。
为了解决这个问题,我们可以在配置文件中开启事务管理器或使用数据源代理。
示例二: 抛出Checked Exception
在该例子中,我们假设数据库中已经存在一个数据,我们在新增数据时抛出一个Checked Exception,并期望两条insert语句都不会提交到数据库。
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
@Transactional
public boolean addUsers() throws SQLException{
User user1 = new User("chenweitao", 18, 99.99);
userMapper.insert(user1);
User user2 = new User("xiaoxia", 20, 100.0);
throw new SQLException("Insert Failed!");// 抛出一个Checked Exception
//userMapper.insert(user2); // 注释掉该行
//return true; // 注释掉该行
}
}
此时,因为我们使用了事务注解,并抛出了一个Checked Exception,事务应该回滚,但是实际上两条insert语句都已提交到数据库。这是因为Spring默认只会对Uncheked Exception进行事务回滚,而不会对Checked Exception进行回滚。
为了解决这个问题,我们需要在@Transactional注解中添加rollbackFor参数,指定需要回滚的Exception,如下所示:
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
@Transactional(rollbackFor = SQLException.class)
public boolean addUsers() throws SQLException {
User user1 = new User("chenweitao", 18, 99.99);
userMapper.insert(user1);
User user2 = new User("xiaoxia", 20, 100.0);
throw new SQLException("Insert Failed!");// 抛出一个Checked Exception
}
}
在上面示例中我们在@Transactional注解中添加了rollbackFor = SQLException.class,这就确保了在发生SQLException的时候也能回滚事务。
以上就是关于解决@Transactional注解事务不回滚不起作用的问题的攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:解决@Transactional注解事务不回滚不起作用的问题 - Python技术站