罗将军的恩情还不完 發表於 2025-6-10 20:52:00

@Transactional 什么情况下会失效

<p><span data-cke-copybin-start="1">​</span><strong>@Transactional</strong> 注解在 Spring 中用于声明式事务管理,但在某些场景下会失效。</p>
<h3><strong>1、方法非 public 修饰</strong></h3>
<ul>
<li><strong>原因:</strong>Spring AOP 代理(CGLIB/JDK 动态代理)无法拦截 private/protected 的方法。</li>
<li><strong>解决:</strong>确保事务方法为 public。</li>
</ul>
<pre class="language-java highlighter-hljs"><code>// ✅ 正确
@Transactional
public void createUser() { ... }

// ❌ 失效
@Transactional
private void createUser() { ... }</code></pre>
<h3><strong>2、自调用问题(同类内调用)</strong></h3>
<ul>
<li><strong>原因:</strong>类内部方法调用(如 <strong>A.a()</strong> 调用 <strong>A.b()</strong>)会绕过代理对象,导致 @Transactional 失效。</li>
<li><strong>解决:</strong></li>
</ul>
<p><strong>&nbsp; &nbsp; &nbsp; &nbsp; ①&nbsp;</strong>注入自身代理对象:<strong>@Autowired private MyService self;</strong> 后调用 <strong>self.b()</strong>。</p>
<p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;②&nbsp;</strong>通过 AopContext 获取代理:((MyService) AopContext.currentProxy()).b()(需开启 @EnableAspectJAutoProxy(exposeProxy = true))。我更喜欢把逻辑写到另一个类中,然后再进行调用。</p>
<pre class="language-java highlighter-hljs"><code>@Service
public class UserService {
    @Autowired
    private UserService self; // 注入自身代理

    public void methodA() {
      // ❌ 直接调用失效
      // methodB();

      // ✅ 通过代理调用生效
      self.methodB();
    }

    @Transactional
    public void methodB() { ... }
}</code></pre>
<h3><strong>3、异常类型未被捕获</strong></h3>
<ul>
<li><strong>原因:</strong></li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>①</strong> 默认只捕获&nbsp;<strong><code>RuntimeException</code>&nbsp;</strong>和&nbsp;<strong><code>Error</code></strong>。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>②</strong> 若抛出&nbsp;<strong><code>IOException</code>&nbsp;</strong>等<strong>受检异常</strong>,事务不会回滚。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>③</strong> 若异常被&nbsp;<strong><code>catch</code>&nbsp;</strong>后未重新抛出,事务失效。</p>
<ul>
<li><strong>解决:</strong></li>
</ul>
<p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;①&nbsp;</strong>使用&nbsp;<strong><code>rollbackFor</code>&nbsp;</strong>指定异常类型:<strong><code>@Transactional(rollbackFor = Exception.class)</code></strong></p>
<p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;②</strong> 避免在方法内吞没异常。</p>
<pre class="language-java highlighter-hljs"><code>// ✅ 回滚受检异常
@Transactional(rollbackFor = Exception.class)
public void update() throws IOException {
    try {
      // 数据库操作
    } catch (Exception e) {
      // ❌ 错误:吞没异常
      // log.error(e);

      // ✅ 正确:重新抛出
      throw new BusinessException(e);
    }
}</code></pre>
<h3><strong>4、数据库引擎不支持事务</strong></h3>
<ul>
<li>
<p><strong>原因</strong>:如 MySQL 的&nbsp;<strong>MyISAM 引擎</strong>不支持事务(仅 InnoDB 支持)。</p>
</li>
<li>
<p><strong>解决</strong>:确认使用支持事务的引擎(比如 InnoDB 引擎)。</p>
</li>
</ul>
<h3><strong>5、事务传播行为配置不当</strong></h3>
<ul>
<li>
<p><strong>原因</strong>:若内层方法使用&nbsp;<strong><code>Propagation.NOT_SUPPORTED</code></strong>&nbsp;或&nbsp;<strong><code>Propagation.NEVER</code></strong>&nbsp;等传播行为,会挂起外层事务。</p>
</li>
<li>
<p><strong>解决</strong>:根据业务需求调整传播行为(如&nbsp;<strong><code>Propagation.REQUIRED</code></strong>)。</p>
</li>
</ul>
<pre class="language-java highlighter-hljs"><code>// ❌ 内层方法挂起事务
@Transactional(propagation = Propagation.NEVER)
public void innerMethod() { ... }</code></pre>
<h3><strong>6、多线程调用(</strong>异步调用<strong>)</strong></h3>
<ul>
<li>
<p><strong>原因</strong>:新线程内执行数据库操作不归属同一事务(事务绑定到 ThreadLocal)。异步方法(如使用 <strong><code>@Async</code> </strong>注解的方法)不会参与当前事务。即使该方法被 <code>@Transactional</code> 注解标记,事务也不会生效。</p>
</li>
<li>
<p><strong>解决</strong>:避免跨线程操作,或手动传递事务上下文(比较复杂)。</p>
</li>
</ul>
<h3><strong>7、非 Spring 管理的 Bean</strong></h3>
<ul>
<li>
<p><strong>原因</strong>:通过&nbsp;<strong><code>new</code>&nbsp;</strong>创建的对象不受 Spring 代理管理。</p>
</li>
<li>
<p><strong>解决</strong>:确保对象由 Spring 容器创建(如&nbsp;<strong><code>@Component</code></strong>)。</p>
</li>
</ul>
<h3><strong>8、方法 final 或 static</strong></h3>
<ul>
<li>
<p><strong>原因</strong>:CGLIB 无法代理&nbsp;<strong><code>final</code>/<code>static</code></strong>&nbsp;方法(JDK 代理无法代理 static)。</p>
</li>
<li>
<p><strong>解决</strong>:避免修饰事务方法为&nbsp;<strong><code>final</code>&nbsp;</strong>或&nbsp;<strong><code>static</code></strong>。</p>
</li>
</ul>
<h3><strong>9、未启用事务管理</strong></h3>
<ul>
<li>
<p><strong>原因</strong>:忘记添加&nbsp;<strong><code>@EnableTransactionManagement</code></strong>(Spring Boot 中自动配置)。</p>
</li>
<li>
<p><strong>解决</strong>:检查配置类是否启用事务管理。</p>
</li>
</ul>
<h3><strong>10、非事务性方法调用</strong></h3>
<ul>
<li>
<p><strong>原因</strong>:如果在事务方法内部调用了另一个<strong>没有事务管理的方法</strong>,事务将不会传播到被调用的方法中。事务只会在当前方法的调用栈中生效。</p>
</li>
<li>
<p><strong>解决</strong>:确保所有需要事务的操作都在事务方法中。</p>
</li>
</ul>
<p>这些都是 <code>@Transactional</code> 可能失效的常见原因。如果遇到事务失效的问题,可以逐一排查这些情况。在工作中,有时出现了数据不一致的情况,去排查才发现是事务失效了。</p>
<p style="text-align: right"><span style="color: rgba(53, 152, 219, 1)">知是行之始,行是知之成。-- 烟沙九洲</span></p><br><br>
来源:https://www.cnblogs.com/yanshajiuzhou/p/18922768
頁: [1]
查看完整版本: @Transactional 什么情况下会失效