SpringBoot开启事务常见坑点

序言:此前,我们主要通过XML配置Spring来托管事务。在SpringBoot则非常简单,

只需在业务层添加事务注解(@Transactional )即可快速开启事务。虽然事务很简单,但对于数据方面是需要谨慎对待的,识别常见坑点对我们开发有帮助。

 

 

1. 引入依赖

    <!--依赖管理 -->
    <dependencies>
        <dependency> <!--添加Web依赖 -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency> <!--添加Mybatis依赖 -->
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency><!--添加MySQL驱动依赖 -->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency><!--添加Test依赖 -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2. 添加配置

主要是配置数据源和开启Mybatis的自动驼峰映射

@SpringBootApplication
public class MybatisTransactionApplication {

    public static void main(String[] args) {
        //1.初始化
        SpringApplication application=  new SpringApplication(MybatisTransactionApplication.class);

        //2.添加数据源
        Map<String,Object> map = new HashMap<>();
        map.put("spring.datasource.url","jdbc:mysql://localhost:3306/socks?useSSL=false");
        map.put("spring.datasource.username","root");
        map.put("spring.datasource.password","root");

        //3.开启驼峰映射 (Such as account_id ==> accountId)
        map.put("mybatis.configuration.map-underscore-to-camel-case",true);
        application.setDefaultProperties(map);

        //4.启动应用
        application.run(args);
    }
}

3. 添加数据库记录

打开 Navicat 的查询窗口,然后执行以下SQL:

DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `account_id` varchar(30) ,
  `account_name` varchar(30),
  `balance` decimal(20,2),
  PRIMARY KEY (`account_id`)
);

insert into account values ('1','admin','1000.25');

执行完毕后,可以查询到账户数据,如图:

4. 编写代码

以操作账户金额为例,模拟正常操作金额提交事务,以及发生异常回滚事务。
其中控制层代码如下:

package com.hehe.controller;

@RestController
public class AccountController {

    @SuppressWarnings("all")
    @Autowired
    AccountService accountService;

    @GetMapping("/")
    public Account getAccount() {
        //查询账户
        return accountService.getAccount();
    }

    @GetMapping("/add")
    public Object addMoney() {
        try {
            accountService.addMoney();
        } catch (Exception e) {
            return "发生异常了:" + accountService.getAccount();
        }
        return getAccount();
    }
}

在业务层使用 @Transactional 开启事务,执行数据库操作后抛出异常。具体代码如下:

package com.hehe.service;

@Service
public class AccountService {

    @SuppressWarnings("all")
    @Autowired
    AccountMapper accountMapper;

    public Account getAccount() {
        return accountMapper.getAccount();
    }

    @Transactional
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //然后遇到故障
        throw new RuntimeException("发生异常了..");
    }
}

数据库层就很简单了,我们通过注解来实现账户数据的查询,具体如下:

package com.hehe.mapper;

@Mapper
public interface AccountMapper {

    @Select("select * from account where account_id=1")
    Account getAccount();

    @Update("update account set balance = balance+100 where account_id=1")
    void addMoney();
}

其中 Account 实体对象如下:

package com.hehe.pojo;

public class Account {

    private String accountId;
    private String accountName;
    private BigDecimal balance;

    // Override toString Method ..
    // Getter & Setters  ..
}

5. 测试事务

启动应用,访问 http://localhost:8080 ,可以看到账户数据,如下:

然后访问 http://localhost:8080/add ,可以看到账户余额并没有增加,如下: 也就是说事务开启成功,数据得到回滚。

6. 常见坑点

使用事务注解@Transactional 之前,应该先了解它的相关属性,避免在实际项目中踩中各种各样的坑点。

常见坑点1:遇到非检测异常时,事务不开启,也无法回滚。

例如下面这段代码,账户余额依旧增加成功,并没有因为后面遇到检测异常而回滚!!

   @Transactional
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //然后遇到故障
        throw new SQLException("发生异常了..");
    }

原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对非检测异常进行事务回滚,可以在@Transactional 注解里使用
rollbackFor 属性明确指定异常。例如下面这样,就可以正常回滚:

  @Transactional(rollbackFor = Exception.class)
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //然后遇到故障
        throw new SQLException("发生异常了..");
    }

常见坑点2: 在业务层捕捉异常后,发现事务不生效。

这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。例如:下面这段代码直接导致增加余额的事务回滚没有生效。

可以在异常中使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 手动回滚

    @Transactional
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //谨慎:尽量不要在业务层捕捉异常并处理
        try {
            throw new SQLException("发生异常了..");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

不要小瞧了这些细节,往前暴露异常很大程度上很能够帮我们快速定位问题,而不是经常在项目上线后出现问题,却无法刨根知道哪里报错。

推荐做法:在业务层统一抛出异常,然后在控制层统一处理。

    @Transactional
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //推荐:在业务层将异常抛出
        throw new RuntimeException("发生异常了..");
    }

好用的框架随处可见,高效的整合万里挑一,关注作者,关注SpringBoot,让Java编程更简单!!

赞赏支持



作者:yizhiwazi
链接:https://www.jianshu.com/p/380a9d980ca5
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 

<think>嗯,用户问的是“SpringBoot车祸现场是指什么”,我需要先理解这个问题的背景。首先,“车祸现场”在中文网络用语中通常用来比喻某个事情出现了严重的问题或混乱的情况,带有调侃或自嘲的意味。所以用户可能是在问SpringBoot开发中常见的错误或者容易出问题的地方。 接下来,我需要回忆一下SpringBoot相关的常见问题。SpringBoot虽然简化了配置,但开发者在使用时还是会遇到各种。比如自动配置的冲突,版本依赖的问题,配置文件的错误,Bean的注入问题,事务管理不当,缓存配置错误等等。这些都是可能导致应用崩溃或者运行异常的情况,符合“车祸现场”的描述。 然后,我需要考虑用户可能的背景。用户可能是刚接触SpringBoot的开发者,遇到了问题,或者在社区里看到相关术语,想了解具体指哪些情况。因此,回答需要结构清晰,分说明,每个问题给出例子和解决方法,帮助用户避免这些错误。 另外,用户提供的系统级指令要求使用中文回答,数学公式用$...$格式,独立公式用$$...$$,但这个问题可能不涉及数学公式。不过需要注意回答的结构,确保每个部分明确,比如分列出常见的“车祸现场”场景,每个场景下详细说明原因和解决方案。 还需要验证信息的准确性,比如自动配置的原理,版本依赖如何解决,配置文件的优先级,Bean的作用域等,确保回答内容正确可靠。同时,语言要口语化,避免技术术语过于晦涩,让新手也能理解。 最后,总结部分需要强调这些问题是开发中的常见陷阱,通过经验积累和最佳实践可以避免,这样用户能有一个正面的收获,知道如何应对这些问题。</think>SpringBoot "车祸现场" 是开发者社群中常用的调侃说法,主要指在 SpringBoot 开发过程中因配置不当、技术理解偏差或常见错误导致的严重问题或混乱场景。以下是典型场景及技术解析: --- ### 1. **自动配置冲突** **场景**: 同时引入`spring-boot-starter-data-jpa`和`spring-boot-starter-data-mongodb`未指定主数据源,启动时报`No qualifying bean of type 'javax.sql.DataSource'`。 **原理**: SpringBoot 的`@ConditionalOnClass`注解触发多个数据源自动配置,但未通过`@Primary`标记主 Bean。 **解决方案**: ```java @Configuration public class DataSourceConfig { @Bean @Primary @ConfigurationProperties(prefix="spring.datasource") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } } ``` --- ### 2. **版本依赖雪崩** **场景**: 引入第三方库如`xxl-job-core 2.3.0`导致 SpringBoot 2.7.x 项目启动失败,报`MethodNotFoundException`。 **根本原因**: 库内部依赖的 Spring 版本与项目主版本不兼容,通过 Maven 依赖树可验证: ```bash mvn dependency:tree -Dincludes=org.springframework ``` **解决步骤**: 1. 显式声明兼容版本依赖 2. 使用`<exclusions>`排除冲突依赖 3. 或升级第三方库到适配版本 --- ### 3. **配置文件优先级陷阱** **错误现象**: 同时存在`application.yml`和`bootstrap.yml`时,Nacos 配置中心加载失败。 **优先级规则**: SpringBoot 配置加载顺序为: 1. `bootstrap.properties` (高) 2. `application.properties` 3. `bootstrap.yml` 4. `application.yml` (低) **正确实践**: 分布式配置应写在`bootstrap.yml`中,且需确保引入`spring-cloud-starter-bootstrap`依赖。 --- ### 4. **循环依赖死锁** **典型报错**: `The dependencies of some of the beans in the application context form a cycle` **代码示例**: ```java @Service class AService { @Autowired BService b; // 类A依赖类B } @Service class BService { @Autowired AService a; // 类B反向依赖类A } ``` **解决方案**: - 使用`@Lazy`延迟加载 - 重构代码结构,引入 DTO 解耦 - SpringBoot 2.6+ 默认禁止循环依赖,可通过`spring.main.allow-circular-references=true`临时绕过(不推荐) --- ### 5. **事务传播误解** **隐蔽 Bug**: 在`@Transactional`方法内调用同类其他方法导致事务失效。 **技术原理**: Spring 事务基于 AOP 代理实现,自调用会绕过代理机制。 **修正方案**: ```java @Service public class OrderService { public void outerMethod() { innerMethod(); // 错误:直接调用不走代理 ((OrderService)AopContext.currentProxy()).innerMethod(); // 正确:通过代理调用 } @Transactional public void innerMethod() { // 数据库操作 } } ``` --- ### 6. **Actuator 信息泄露** **安全漏洞**: 未配置`management.endpoints.web.exposure.include`时,默认暴露`/actuator/health`以外的端。 **风险案例**: 攻击者访问`/actuator/env`可获取数据库密码等敏感信息。 **加固方案**: ```yaml management: endpoints: web: exposure: include: health,info endpoint: env: enabled: false beans: enabled: false ``` --- ### 经验总结 | 问题类型 | 预防措施 | 调试工具 | |---------|---------|---------| | 配置冲突 | 使用`spring-boot-autoconfigure-processor` | `/actuator/conditions` | | 依赖问题 | 定期执行`mvn dependency:analyze` | Maven Enforcer 插件 | | 事务异常 | 开启调试日志`logging.level.org.springframework.jdbc=DEBUG` | JDBC 日志分析工具 | 通过合理使用 SpringBoot 提供的`/actuator`端、结合 APM 工具(如 SkyWalking)进行运行时监控,可显著降低"车祸"发生概率。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值