面试题答案
一键面试确保操作原子性
在Spring Boot项目中通过Spring的事务管理机制确保多个数据库操作的原子性,主要通过以下步骤:
- 配置事务管理器:
- 对于关系型数据库,如MySQL,通常使用
DataSourceTransactionManager
。在Spring Boot中,若使用Spring Data JPA,会自动配置JpaTransactionManager
。例如,对于DataSourceTransactionManager
配置如下:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; @Configuration public class TransactionConfig { @Autowired private DataSource dataSource; @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource); } }
- 对于关系型数据库,如MySQL,通常使用
- 使用事务注解:
- 在服务方法上使用
@Transactional
注解,Spring会自动管理事务的开始、提交和回滚。例如:
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserService { @Transactional public void complexDatabaseOperations() { // 多个数据库操作 } }
@Transactional
注解的属性可以进一步控制事务行为,如propagation
(事务传播行为)、isolation
(事务隔离级别)等。例如,设置事务传播行为为REQUIRED
(默认),表示如果当前存在事务,则加入该事务;如果不存在,则创建一个新事务:
@Transactional(propagation = Propagation.REQUIRED) public void someMethod() { // 数据库操作 }
- 在服务方法上使用
声明式事务和编程式事务的区别及适用场景
- 区别:
- 声明式事务:通过注解(如
@Transactional
)或XML配置来管理事务,代码侵入性低,只需要在需要事务管理的方法或类上添加注解即可,Spring框架在运行时根据配置来管理事务。例如上述UserService
中的@Transactional
使用方式。 - 编程式事务:通过编写代码来管理事务,通常使用
TransactionTemplate
或PlatformTransactionManager
接口。这种方式代码侵入性高,需要在业务代码中显式地开始、提交和回滚事务。例如:
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; @Service public class AnotherService { @Autowired private TransactionTemplate transactionTemplate; public void someTransactionalMethod() { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { // 数据库操作 try { // 业务逻辑 } catch (Exception e) { status.setRollbackOnly(); } } }); } }
- 声明式事务:通过注解(如
- 适用场景:
- 声明式事务:适用于大多数业务场景,尤其是事务边界比较清晰,在方法级别进行事务管理即可满足需求的情况。例如普通的增删改查业务逻辑。
- 编程式事务:适用于事务管理比较复杂,需要在代码中动态控制事务边界的场景,如在循环中根据不同条件决定是否提交或回滚事务。
事务管理过程中常见问题及解决方案
- 事务未生效:
- 原因:
- 方法未被Spring容器管理,例如在普通Java类中调用带有
@Transactional
注解的方法,而不是通过Spring注入的方式调用。 - 方法为非public,
@Transactional
注解默认只对public方法生效。 - 自调用问题,即同一个类中一个方法调用另一个带有
@Transactional
注解的方法,这种情况下事务不会生效,因为Spring的事务代理是基于AOP,自调用不会经过代理对象。
- 方法未被Spring容器管理,例如在普通Java类中调用带有
- 解决方案:
- 确保相关类被Spring容器管理,通过
@Component
、@Service
等注解标注。 - 将带有
@Transactional
注解的方法设置为public。 - 解决自调用问题,可以通过注入自身(在Spring Boot 2.0+可使用
@Lazy
注解避免循环依赖),或者将事务方法抽取到另一个类中。
- 确保相关类被Spring容器管理,通过
- 原因:
- 事务传播行为问题:
- 原因:对事务传播行为配置不当,例如需要嵌套事务时未正确设置
propagation
属性。 - 解决方案:根据业务需求正确配置
@Transactional
注解的propagation
属性,如Propagation.REQUIRES_NEW
用于创建新事务,Propagation.NESTED
用于创建嵌套事务等。
- 原因:对事务传播行为配置不当,例如需要嵌套事务时未正确设置
- 事务隔离级别问题:
- 原因:默认的事务隔离级别(如
READ_COMMITTED
)可能无法满足业务需求,导致脏读、不可重复读或幻读等问题。 - 解决方案:根据业务场景设置合适的事务隔离级别,如
@Transactional(isolation = Isolation.SERIALIZABLE)
可避免所有并发问题,但会降低系统并发性能,所以需要权衡选择。
- 原因:默认的事务隔离级别(如