<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Spring on 吾爱主机</title><link>https://blog.waihost.com/categories/spring/</link><description>Recent content in Spring on 吾爱主机</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 17 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://blog.waihost.com/categories/spring/index.xml" rel="self" type="application/rss+xml"/><item><title>Spring 事务传播机制：REQUIRED、REQUIRES_NEW 与嵌套事务工程实践</title><link>https://blog.waihost.com/posts/spring-transaction-propagation-practice/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/spring-transaction-propagation-practice/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/spring-transaction-propagation-practice.svg" alt="Featured image of post Spring 事务传播机制：REQUIRED、REQUIRES_NEW 与嵌套事务工程实践" /&gt;&lt;p&gt;业务代码里最容易“看起来对、跑起来却不对”的一类问题，就是 &lt;strong&gt;事务边界&lt;/strong&gt;。方法上贴了 &lt;code&gt;@Transactional&lt;/code&gt;，并不等于“想回滚就一定回滚、想独立提交就一定独立提交”。真正决定行为的，是 &lt;strong&gt;传播行为（propagation）&lt;/strong&gt;、&lt;strong&gt;物理事务 vs 逻辑事务&lt;/strong&gt;、以及 Spring 默认的 &lt;strong&gt;代理拦截模型&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;本文基于 Spring Framework 官方事务文档，把 &lt;code&gt;REQUIRED&lt;/code&gt; / &lt;code&gt;REQUIRES_NEW&lt;/code&gt; / &lt;code&gt;NESTED&lt;/code&gt; 等传播语义、回滚规则与常见工程踩坑讲清楚，并给出可落地的写法与排查清单。&lt;/p&gt;
&lt;h2 id="一先分清物理事务-vs-逻辑事务"&gt;&lt;a href="#%e4%b8%80%e5%85%88%e5%88%86%e6%b8%85%e7%89%a9%e7%90%86%e4%ba%8b%e5%8a%a1-vs-%e9%80%bb%e8%be%91%e4%ba%8b%e5%8a%a1" class="header-anchor"&gt;&lt;/a&gt;一、先分清：物理事务 vs 逻辑事务
&lt;/h2&gt;&lt;p&gt;Spring 管理事务时，不能只看“有没有 &lt;code&gt;@Transactional&lt;/code&gt;”，而要区分两层：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;概念&lt;/th&gt;
					&lt;th&gt;含义&lt;/th&gt;
					&lt;th&gt;工程直觉&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;物理事务（physical）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;底层资源上真正开启的事务（如一条 JDBC Connection 上的 &lt;code&gt;begin/commit/rollback&lt;/code&gt;）&lt;/td&gt;
					&lt;td&gt;能不能真正提交/回滚、锁何时释放&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;逻辑事务（logical）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;每个被 Spring 事务拦截的方法作用域&lt;/td&gt;
					&lt;td&gt;该方法能否单独标记 &lt;code&gt;rollback-only&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;传播行为决定的是：&lt;strong&gt;外层方法与内层方法之间，逻辑作用域如何映射到物理事务&lt;/strong&gt;。&lt;br&gt;
这正是很多“内层抛异常后外层还以为自己 commit 成功了”的根源。&lt;/p&gt;
&lt;h2 id="二七种传播行为速查"&gt;&lt;a href="#%e4%ba%8c%e4%b8%83%e7%a7%8d%e4%bc%a0%e6%92%ad%e8%a1%8c%e4%b8%ba%e9%80%9f%e6%9f%a5" class="header-anchor"&gt;&lt;/a&gt;二、七种传播行为速查
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;@Transactional(propagation = …)&lt;/code&gt; 对应 &lt;code&gt;TransactionDefinition&lt;/code&gt; 中的常量。按“有没有外层事务”理解最稳：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;传播行为&lt;/th&gt;
					&lt;th&gt;无外层事务时&lt;/th&gt;
					&lt;th&gt;有外层事务时&lt;/th&gt;
					&lt;th&gt;典型用途&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;REQUIRED&lt;/code&gt;（默认）&lt;/td&gt;
					&lt;td&gt;新建物理事务&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;加入&lt;/strong&gt;外层同一物理事务&lt;/td&gt;
					&lt;td&gt;服务门面 + 多仓储同事务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;SUPPORTS&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;非事务执行&lt;/td&gt;
					&lt;td&gt;加入外层事务&lt;/td&gt;
					&lt;td&gt;查询既可独立也可参与写事务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;MANDATORY&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;抛异常&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;加入外层事务&lt;/td&gt;
					&lt;td&gt;强制调用方必须已开事务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;REQUIRES_NEW&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;新建物理事务&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;挂起外层&lt;/strong&gt;，新建独立物理事务&lt;/td&gt;
					&lt;td&gt;审计日志、独立提交的状态点&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;NOT_SUPPORTED&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;非事务执行&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;挂起外层&lt;/strong&gt;，非事务执行&lt;/td&gt;
					&lt;td&gt;长耗时/远程调用不想占连接&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;NEVER&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;非事务执行&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;抛异常&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;明确禁止在事务中调用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;NESTED&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;等同 &lt;code&gt;REQUIRED&lt;/code&gt; 新建&lt;/td&gt;
					&lt;td&gt;同一物理事务内建 &lt;strong&gt;savepoint&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;内层可局部回滚，外层可继续&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;日常工程里真正高频的是：&lt;strong&gt;&lt;code&gt;REQUIRED&lt;/code&gt;、&lt;code&gt;REQUIRES_NEW&lt;/code&gt;、&lt;code&gt;NESTED&lt;/code&gt;&lt;/strong&gt;。下面展开它们的语义差异。&lt;/p&gt;
&lt;h2 id="三required默认正确但要懂-unexpectedrollbackexception"&gt;&lt;a href="#%e4%b8%89required%e9%bb%98%e8%ae%a4%e6%ad%a3%e7%a1%ae%e4%bd%86%e8%a6%81%e6%87%82-unexpectedrollbackexception" class="header-anchor"&gt;&lt;/a&gt;三、REQUIRED：默认正确，但要懂 UnexpectedRollbackException
&lt;/h2&gt;&lt;h3 id="1-语义"&gt;&lt;a href="#1-%e8%af%ad%e4%b9%89" class="header-anchor"&gt;&lt;/a&gt;1. 语义
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;PROPAGATION_REQUIRED&lt;/code&gt; 会 &lt;strong&gt;强制存在物理事务&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当前没有事务 → 为本方法新建；&lt;/li&gt;
&lt;li&gt;已有外层事务 → &lt;strong&gt;参与&lt;/strong&gt;该外层物理事务（同一连接、同一提交点）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这很适合：一个 Service 编排多个 Repository，要求“要么一起成功，要么一起失败”。&lt;/p&gt;
&lt;h3 id="2-逻辑作用域仍是独立的"&gt;&lt;a href="#2-%e9%80%bb%e8%be%91%e4%bd%9c%e7%94%a8%e5%9f%9f%e4%bb%8d%e6%98%af%e7%8b%ac%e7%ab%8b%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;2. 逻辑作用域仍是独立的
&lt;/h3&gt;&lt;p&gt;即便映射到同一物理事务，Spring 仍会为每个 &lt;code&gt;REQUIRED&lt;/code&gt; 方法创建 &lt;strong&gt;逻辑事务作用域&lt;/strong&gt;。每个作用域可以独立设置 &lt;code&gt;rollback-only&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;关键后果（官方明确描述）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;内层逻辑事务标记了 rollback-only；&lt;/li&gt;
&lt;li&gt;外层自己没有决定回滚，仍尝试 commit；&lt;/li&gt;
&lt;li&gt;物理事务最终回滚，外层会收到 &lt;strong&gt;&lt;code&gt;UnexpectedRollbackException&lt;/code&gt;&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这不是“Spring 抽风”，而是保护调用方 &lt;strong&gt;绝不能误以为已经提交成功&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="3-内层-isolation--timeout--readonly-默认会被忽略"&gt;&lt;a href="#3-%e5%86%85%e5%b1%82-isolation--timeout--readonly-%e9%bb%98%e8%ae%a4%e4%bc%9a%e8%a2%ab%e5%bf%bd%e7%95%a5" class="header-anchor"&gt;&lt;/a&gt;3. 内层 isolation / timeout / readOnly 默认会被忽略
&lt;/h3&gt;&lt;p&gt;参与外层事务时，内层声明的隔离级别、超时、只读标志 &lt;strong&gt;默认静默忽略&lt;/strong&gt;，以外层特征为准。&lt;br&gt;
若希望“内层要求与外层不一致时直接失败”，可在事务管理器上打开 &lt;code&gt;validateExistingTransaction=true&lt;/code&gt;（非宽松模式也会拒绝只读/读写不匹配）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 伪配置示意：开启后，内层 isolation/readOnly 与外层冲突会拒绝加入&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// PlatformTransactionManager 具体实现上设置 validateExistingTransaction = true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="四requires_new真正的独立事务"&gt;&lt;a href="#%e5%9b%9brequires_new%e7%9c%9f%e6%ad%a3%e7%9a%84%e7%8b%ac%e7%ab%8b%e4%ba%8b%e5%8a%a1" class="header-anchor"&gt;&lt;/a&gt;四、REQUIRES_NEW：真正的“独立事务”
&lt;/h2&gt;&lt;h3 id="1-语义-1"&gt;&lt;a href="#1-%e8%af%ad%e4%b9%89-1" class="header-anchor"&gt;&lt;/a&gt;1. 语义
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;PROPAGATION_REQUIRES_NEW&lt;/code&gt; &lt;strong&gt;总是&lt;/strong&gt;开启独立物理事务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有外层事务 → &lt;strong&gt;挂起&lt;/strong&gt;外层，内层用新资源事务；&lt;/li&gt;
&lt;li&gt;内层 commit/rollback &lt;strong&gt;不影响&lt;/strong&gt;外层事务的提交结果；&lt;/li&gt;
&lt;li&gt;内层结束后，其持有的锁会尽快释放；&lt;/li&gt;
&lt;li&gt;内层可使用自己的 isolation / timeout / readOnly。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-适合什么"&gt;&lt;a href="#2-%e9%80%82%e5%90%88%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;2. 适合什么
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;操作日志 / 审计记录：业务主事务回滚，日志仍要留下；&lt;/li&gt;
&lt;li&gt;状态机“已到达某检查点”的独立落库；&lt;/li&gt;
&lt;li&gt;需要更短锁持有时间、且允许与外层最终结果不一致的写路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-连接池风险必须重视"&gt;&lt;a href="#3-%e8%bf%9e%e6%8e%a5%e6%b1%a0%e9%a3%8e%e9%99%a9%e5%bf%85%e9%a1%bb%e9%87%8d%e8%a7%86" class="header-anchor"&gt;&lt;/a&gt;3. 连接池风险（必须重视）
&lt;/h3&gt;&lt;p&gt;官方明确警告：外层事务仍绑定着资源时，内层还要再拿 &lt;strong&gt;新的数据库连接&lt;/strong&gt;。若连接池偏小，可能出现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;连接池耗尽；&lt;/li&gt;
&lt;li&gt;多线程都在外层事务中等待内层连接 → &lt;strong&gt;死锁式等待&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实践建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;连接池最大连接数要 &lt;strong&gt;大于并发线程数&lt;/strong&gt;（官方表述：至少比并发线程数多 1，实际应按峰值嵌套深度留余量）；&lt;/li&gt;
&lt;li&gt;不要在循环体里高频 &lt;code&gt;REQUIRES_NEW&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;先问自己：真的需要“独立提交”，还是只是想“内层失败不要拖死外层”——后者可能 &lt;code&gt;NESTED&lt;/code&gt; 更合适。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AuditService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;auditService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderRepository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 默认 REQUIRED&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderCmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 审计希望独立提交：业务失败也不丢“尝试下单”痕迹&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;auditService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;recordAttempt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;PLACE_ORDER&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ... 后续业务可能抛异常导致外层回滚&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;markPaid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuditService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Propagation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REQUIRES_NEW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;recordAttempt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 独立物理事务：这里的提交不会被外层回滚“带走”&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="五nested同一物理事务上的-savepoint"&gt;&lt;a href="#%e4%ba%94nested%e5%90%8c%e4%b8%80%e7%89%a9%e7%90%86%e4%ba%8b%e5%8a%a1%e4%b8%8a%e7%9a%84-savepoint" class="header-anchor"&gt;&lt;/a&gt;五、NESTED：同一物理事务上的 savepoint
&lt;/h2&gt;&lt;h3 id="1-语义-2"&gt;&lt;a href="#1-%e8%af%ad%e4%b9%89-2" class="header-anchor"&gt;&lt;/a&gt;1. 语义
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;PROPAGATION_NESTED&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;一个物理事务&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;内层通过 &lt;strong&gt;savepoint（保存点）&lt;/strong&gt; 支持局部回滚；&lt;/li&gt;
&lt;li&gt;内层回滚到 savepoint 后，&lt;strong&gt;外层仍可继续&lt;/strong&gt;并最终 commit/rollback。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它通常映射到 &lt;strong&gt;JDBC savepoint&lt;/strong&gt;，因此：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;依赖 JDBC 资源事务；&lt;/li&gt;
&lt;li&gt;与 &lt;code&gt;DataSourceTransactionManager&lt;/code&gt; 一类管理器配合最常见；&lt;/li&gt;
&lt;li&gt;不是所有事务管理器/资源都支持嵌套语义。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-与-requires_new-的选型"&gt;&lt;a href="#2-%e4%b8%8e-requires_new-%e7%9a%84%e9%80%89%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;2. 与 REQUIRES_NEW 的选型
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;&lt;code&gt;REQUIRES_NEW&lt;/code&gt;&lt;/th&gt;
					&lt;th&gt;&lt;code&gt;NESTED&lt;/code&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;物理事务&lt;/td&gt;
					&lt;td&gt;独立两条&lt;/td&gt;
					&lt;td&gt;仍是一条&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;内层回滚&lt;/td&gt;
					&lt;td&gt;不影响外层提交决策&lt;/td&gt;
					&lt;td&gt;只回滚到 savepoint，外层可继续&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;内层提交可见性&lt;/td&gt;
					&lt;td&gt;内层结束即可对外可见（独立提交）&lt;/td&gt;
					&lt;td&gt;仍受外层最终 commit 约束&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;连接占用&lt;/td&gt;
					&lt;td&gt;可能额外占连接&lt;/td&gt;
					&lt;td&gt;一般不另开连接&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;资源支持&lt;/td&gt;
					&lt;td&gt;广泛&lt;/td&gt;
					&lt;td&gt;依赖 savepoint 支持&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;口诀：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;要“内层 &lt;strong&gt;真正提交&lt;/strong&gt;，外层怎么回滚都保留” → &lt;code&gt;REQUIRES_NEW&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;要“内层失败可局部撤销，但最终仍跟外层同生共死” → &lt;code&gt;NESTED&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="六回滚规则默认只认-runtimeexception--error"&gt;&lt;a href="#%e5%85%ad%e5%9b%9e%e6%bb%9a%e8%a7%84%e5%88%99%e9%bb%98%e8%ae%a4%e5%8f%aa%e8%ae%a4-runtimeexception--error" class="header-anchor"&gt;&lt;/a&gt;六、回滚规则：默认只认 RuntimeException / Error
&lt;/h2&gt;&lt;p&gt;声明式事务的默认策略（官方 &lt;code&gt;@Transactional&lt;/code&gt; 默认值）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传播：&lt;code&gt;REQUIRED&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;隔离：&lt;code&gt;ISOLATION_DEFAULT&lt;/code&gt;（交给底层数据源默认）&lt;/li&gt;
&lt;li&gt;读写：可写&lt;/li&gt;
&lt;li&gt;超时：底层默认 / 无&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回滚触发&lt;/strong&gt;：&lt;code&gt;RuntimeException&lt;/code&gt; 与 &lt;code&gt;Error&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;默认不回滚&lt;/strong&gt;：受检异常（checked &lt;code&gt;Exception&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这解释了一个经典坑：业务方法 &lt;code&gt;throws BusinessException extends Exception&lt;/code&gt;，调用方 catch 后发现 &lt;strong&gt;数据居然提交了&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;可显式配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rollbackFor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BusinessException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 受检业务异常也会触发回滚&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;noRollbackFor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;InventoryNotEnoughException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;reserveStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;skuId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 某些“预期内”的运行时异常不希望回滚时使用（务必非常克制）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也可在 catch 后程序化标记：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NoProductInStockException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TransactionAspectSupport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTransactionStatus&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setRollbackOnly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;官方仍建议优先用声明式 &lt;code&gt;rollbackFor&lt;/code&gt; / &lt;code&gt;noRollbackFor&lt;/code&gt;，少用手写 &lt;code&gt;setRollbackOnly()&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="七代理模型自调用为什么注解失效"&gt;&lt;a href="#%e4%b8%83%e4%bb%a3%e7%90%86%e6%a8%a1%e5%9e%8b%e8%87%aa%e8%b0%83%e7%94%a8%e4%b8%ba%e4%bb%80%e4%b9%88%e6%b3%a8%e8%a7%a3%e5%a4%b1%e6%95%88" class="header-anchor"&gt;&lt;/a&gt;七、代理模型：自调用为什么“注解失效”
&lt;/h2&gt;&lt;p&gt;默认 &lt;strong&gt;proxy 模式&lt;/strong&gt;（非 AspectJ 编织）下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;只有 &lt;strong&gt;通过代理进来的外部调用&lt;/strong&gt; 才会被拦截；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;同类自调用&lt;/strong&gt;（&lt;code&gt;this.otherTxMethod()&lt;/code&gt;）不会走代理 → 内层 &lt;code&gt;@Transactional&lt;/code&gt; &lt;strong&gt;不生效&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;初始化阶段（如 &lt;code&gt;@PostConstruct&lt;/code&gt;）也不应依赖事务代理；&lt;/li&gt;
&lt;li&gt;Spring Framework 6.0 起，基于类的代理默认可对 &lt;code&gt;protected&lt;/code&gt; / 包可见方法生效；但 &lt;strong&gt;基于接口的 JDK 代理&lt;/strong&gt; 仍要求方法在接口中且为 &lt;code&gt;public&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PayService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 错误：自调用，markPaid 上的 REQUIRES_NEW 不会生效&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;markPaid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Propagation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REQUIRES_NEW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;markPaid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;常见修法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把需要独立传播的方法拆到 &lt;strong&gt;另一个 Spring Bean&lt;/strong&gt;，通过注入调用；&lt;/li&gt;
&lt;li&gt;注入自身代理（&lt;code&gt;@Lazy&lt;/code&gt; 自注入 / &lt;code&gt;AopContext.currentProxy()&lt;/code&gt;，后者需暴露代理）；&lt;/li&gt;
&lt;li&gt;切换到 AspectJ 模式（成本更高，团队要统一约定）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="八可复现的排查清单"&gt;&lt;a href="#%e5%85%ab%e5%8f%af%e5%a4%8d%e7%8e%b0%e7%9a%84%e6%8e%92%e6%9f%a5%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;八、可复现的排查清单
&lt;/h2&gt;&lt;p&gt;当“事务行为不符合预期”时，按下面顺序收敛：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;是不是自调用？&lt;/strong&gt; 打断点看调用栈是否经过 &lt;code&gt;TransactionInterceptor&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异常类型是否触发默认回滚？&lt;/strong&gt; checked vs runtime；是否被上层吞掉。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;传播是否是你以为的那个？&lt;/strong&gt; &lt;code&gt;REQUIRED&lt;/code&gt; 加入外层 ≠ &lt;code&gt;REQUIRES_NEW&lt;/code&gt; 独立提交。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;是否出现 &lt;code&gt;UnexpectedRollbackException&lt;/code&gt;？&lt;/strong&gt; 说明内层已 &lt;code&gt;rollback-only&lt;/code&gt;，外层还在 commit。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连接池是否在 &lt;code&gt;REQUIRES_NEW&lt;/code&gt; 下打满？&lt;/strong&gt; 看获取连接耗时与 active connections。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;只读事务是否被误用于写？&lt;/strong&gt; 以及是否开启了 &lt;code&gt;validateExistingTransaction&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多数据源 / 非 &lt;code&gt;DataSourceTransactionManager&lt;/code&gt; 场景&lt;/strong&gt; 下 &lt;code&gt;NESTED&lt;/code&gt; 是否根本不被支持。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最小实验建议（单测或本地）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;场景 A：outer REQUIRED → inner REQUIRED 抛 RuntimeException
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 期望：整体回滚；外层若吞异常仍可能 UnexpectedRollbackException
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;场景 B：outer REQUIRED → inner REQUIRES_NEW 成功后 outer 回滚
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 期望：inner 已提交数据仍在；outer 变更回滚
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;场景 C：同类 this.inner() 且 inner 标 REQUIRES_NEW
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 期望（proxy 默认）：inner 注解不生效，与 outer 同一逻辑路径
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="九工程建议可直接落到-code-review"&gt;&lt;a href="#%e4%b9%9d%e5%b7%a5%e7%a8%8b%e5%bb%ba%e8%ae%ae%e5%8f%af%e7%9b%b4%e6%8e%a5%e8%90%bd%e5%88%b0-code-review" class="header-anchor"&gt;&lt;/a&gt;九、工程建议（可直接落到 Code Review）
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;默认用 &lt;code&gt;REQUIRED&lt;/code&gt;&lt;/strong&gt;，不要到处 &lt;code&gt;REQUIRES_NEW&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REQUIRES_NEW&lt;/code&gt; 只用于“必须独立提交”的窄路径，并评估连接池。&lt;/li&gt;
&lt;li&gt;需要局部回滚且最终仍同事务提交 → 优先评估 &lt;code&gt;NESTED&lt;/code&gt;，并确认 JDBC savepoint 支持。&lt;/li&gt;
&lt;li&gt;业务异常若走 checked，统一 &lt;code&gt;rollbackFor = Exception.class&lt;/code&gt; 或统一业务异常基类为 runtime。&lt;/li&gt;
&lt;li&gt;事务边界放在 &lt;strong&gt;应用服务层&lt;/strong&gt;，Repository 保持细粒度；避免 Controller 直接开事务导致边界失控。&lt;/li&gt;
&lt;li&gt;长耗时远程调用不要包在大事务里；必要时 &lt;code&gt;NOT_SUPPORTED&lt;/code&gt; 或先缩短事务再调外部。&lt;/li&gt;
&lt;li&gt;Code Review 必问：&lt;strong&gt;谁提交？谁回滚？失败时用户看到的状态与 DB 是否一致？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="十总结"&gt;&lt;a href="#%e5%8d%81%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;十、总结
&lt;/h2&gt;&lt;p&gt;Spring 事务传播不是注解装饰，而是 &lt;strong&gt;逻辑作用域如何映射到物理资源事务&lt;/strong&gt; 的规则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;REQUIRED&lt;/code&gt;：共享物理事务，内层 &lt;code&gt;rollback-only&lt;/code&gt; 会拖垮外层提交预期；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REQUIRES_NEW&lt;/code&gt;：独立物理事务，能独立提交，但要付连接与一致性代价；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NESTED&lt;/code&gt;：同一物理事务上的 savepoint，适合“局部失败、整体仍可继续”的路径；&lt;/li&gt;
&lt;li&gt;回滚默认只盯 &lt;code&gt;RuntimeException&lt;/code&gt;/&lt;code&gt;Error&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;代理模式下 &lt;strong&gt;自调用不走事务拦截&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把这五件事吃透，线上大部分“事务没生效 / 该回没回 / 不该提交却提交”的问题都能快速定位。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Spring Framework Reference — &lt;a class="link" href="https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/tx-propagation.html" target="_blank" rel="noopener"
 &gt;Transaction Propagation&lt;/a&gt;（&lt;code&gt;REQUIRED&lt;/code&gt; / &lt;code&gt;REQUIRES_NEW&lt;/code&gt; / &lt;code&gt;NESTED&lt;/code&gt; 语义与 &lt;code&gt;UnexpectedRollbackException&lt;/code&gt;、连接池警告）&lt;/li&gt;
&lt;li&gt;Spring Framework Reference — &lt;a class="link" href="https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/rolling-back.html" target="_blank" rel="noopener"
 &gt;Rolling Back a Declarative Transaction&lt;/a&gt;（默认 RuntimeException/Error 回滚、&lt;code&gt;rollbackFor&lt;/code&gt;/&lt;code&gt;setRollbackOnly&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;Spring Framework Reference — &lt;a class="link" href="https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/annotations.html" target="_blank" rel="noopener"
 &gt;Using @Transactional&lt;/a&gt;（默认传播/隔离/回滚规则、proxy 自调用限制、可见性）&lt;/li&gt;
&lt;li&gt;Spring Framework Javadoc — &lt;a class="link" href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/TransactionDefinition.html" target="_blank" rel="noopener"
 &gt;TransactionDefinition&lt;/a&gt;（七种 &lt;code&gt;PROPAGATION_*&lt;/code&gt; 常量定义）&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Spring Framework 7.0.8 紧急安全更新：修复 16 个高危漏洞</title><link>https://blog.waihost.com/posts/spring-framework-7-0-8-security-guide/</link><pubDate>Sun, 12 Jul 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/spring-framework-7-0-8-security-guide/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/spring-framework-7-0-8-security-guide.svg" alt="Featured image of post Spring Framework 7.0.8 紧急安全更新：修复 16 个高危漏洞" /&gt;&lt;h2 id="背景介绍"&gt;&lt;a href="#%e8%83%8c%e6%99%af%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;背景介绍
&lt;/h2&gt;&lt;p&gt;Spring 官方在最新发布的 &lt;strong&gt;Spring Framework 7.0.8&lt;/strong&gt; 中，一次性修复了多达 &lt;strong&gt;16 个 CVE 安全漏洞&lt;/strong&gt;。这次更新被称为“AI 时代的 Spring 与安全”系列更新的一部分，修复了包括预测会话 ID、拒绝服务（DoS）、跨站脚本（XSS）、服务器端请求伪造（SSRF）以及不安全的反序列化在内的众多高危问题。&lt;/p&gt;
&lt;p&gt;对于所有使用 Spring Framework 的企业和开发者来说，这是一次&lt;strong&gt;必须立即跟进&lt;/strong&gt;的紧急维护版本。&lt;/p&gt;
&lt;h2 id="核心漏洞清单与原理"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e6%bc%8f%e6%b4%9e%e6%b8%85%e5%8d%95%e4%b8%8e%e5%8e%9f%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;核心漏洞清单与原理
&lt;/h2&gt;&lt;p&gt;本次 7.0.8 版本修复了大量集中在 Spring MVC、WebFlux 和 SpEL (Spring Expression Language) 模块中的安全问题：&lt;/p&gt;
&lt;h3 id="1-web-模块核心漏洞-mvc--webflux"&gt;&lt;a href="#1-web-%e6%a8%a1%e5%9d%97%e6%a0%b8%e5%bf%83%e6%bc%8f%e6%b4%9e-mvc--webflux" class="header-anchor"&gt;&lt;/a&gt;1. Web 模块核心漏洞 (MVC &amp;amp; WebFlux)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CVE-2026-41838&lt;/strong&gt;：WebSocket 模块中会话 ID 可预测问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CVE-2026-41839 / 41840&lt;/strong&gt;：WebFlux 中的会话固定提权与多部分请求（Multipart）拒绝服务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CVE-2026-41841 / 41842 / 41843&lt;/strong&gt;：涉及 Spring MVC 和 WebFlux 的静态资源缓存信息泄露、版本化资源拒绝服务以及路径遍历漏洞。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CVE-2026-41844&lt;/strong&gt;：Spring MVC 和 WebFlux 开放重定向问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-spel-表达式注入与安全限制"&gt;&lt;a href="#2-spel-%e8%a1%a8%e8%be%be%e5%bc%8f%e6%b3%a8%e5%85%a5%e4%b8%8e%e5%ae%89%e5%85%a8%e9%99%90%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;2. SpEL 表达式注入与安全限制
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CVE-2026-41850 / 41851&lt;/strong&gt;：通过 SpEL 表达式导致的算法拒绝服务及无界缓存造成的拒绝服务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CVE-2026-41852&lt;/strong&gt;：SpEL 表达式中的任意方法调用漏洞，这通常是最高危的远程代码执行（RCE）的前置条件。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-其他关键组件"&gt;&lt;a href="#3-%e5%85%b6%e4%bb%96%e5%85%b3%e9%94%ae%e7%bb%84%e4%bb%b6" class="header-anchor"&gt;&lt;/a&gt;3. 其他关键组件
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CVE-2026-41854&lt;/strong&gt;：通过 &lt;code&gt;UriComponentsBuilder&lt;/code&gt; 触发的服务器端请求伪造 (SSRF)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CVE-2026-41855&lt;/strong&gt;：Jackson JMS Converters 存在的不安全反序列化漏洞。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="影响评估"&gt;&lt;a href="#%e5%bd%b1%e5%93%8d%e8%af%84%e4%bc%b0" class="header-anchor"&gt;&lt;/a&gt;影响评估
&lt;/h2&gt;&lt;p&gt;如果你正在使用低于 7.0.8 的 Spring Framework 版本（或对应的 Spring Boot 版本），并启用了上述相关功能（如 WebFlux、WebSocket、SpEL 解析外部输入、静态资源服务），你的应用将面临严重的&lt;strong&gt;信息泄露&lt;/strong&gt;、&lt;strong&gt;拒绝服务&lt;/strong&gt;甚至&lt;strong&gt;系统被控&lt;/strong&gt;风险。&lt;/p&gt;
&lt;p&gt;特别是对外暴露 Web 端口、接受 Multipart 文件上传或者使用 SpEL 处理动态规则的业务系统，受影响最为直接。&lt;/p&gt;
&lt;h2 id="实践建议与修复指南"&gt;&lt;a href="#%e5%ae%9e%e8%b7%b5%e5%bb%ba%e8%ae%ae%e4%b8%8e%e4%bf%ae%e5%a4%8d%e6%8c%87%e5%8d%97" class="header-anchor"&gt;&lt;/a&gt;实践建议与修复指南
&lt;/h2&gt;&lt;h3 id="1-立即升级依赖"&gt;&lt;a href="#1-%e7%ab%8b%e5%8d%b3%e5%8d%87%e7%ba%a7%e4%be%9d%e8%b5%96" class="header-anchor"&gt;&lt;/a&gt;1. 立即升级依赖
&lt;/h3&gt;&lt;p&gt;在你的 &lt;code&gt;pom.xml&lt;/code&gt; 或 &lt;code&gt;build.gradle&lt;/code&gt; 中，将 Spring Framework 的版本强制指定或升级到 &lt;code&gt;7.0.8&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;spring-framework.version&amp;gt;&lt;/span&gt;7.0.8&lt;span class="nt"&gt;&amp;lt;/spring-framework.version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果使用的是 Spring Boot，请密切关注对应 Spring Boot 的最新补丁版本（如 3.5.x 系列的最新安全补丁），因为 Spring Boot 通常会同步打包最新版本的 Spring Framework。&lt;/p&gt;
&lt;h3 id="2-检查危险-api-的使用"&gt;&lt;a href="#2-%e6%a3%80%e6%9f%a5%e5%8d%b1%e9%99%a9-api-%e7%9a%84%e4%bd%bf%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;2. 检查危险 API 的使用
&lt;/h3&gt;&lt;p&gt;除了升级依赖，团队应排查代码库中对以下 API 的直接调用，确保输入被正确校验：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;UriComponentsBuilder&lt;/code&gt; 构建外部传入的 URL 时的 SSRF 防护。&lt;/li&gt;
&lt;li&gt;业务逻辑中涉及的 &lt;code&gt;SpEL&lt;/code&gt; 解析，&lt;strong&gt;严禁将未经净化的用户输入作为表达式内容执行&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-关闭不必要的静态资源服务"&gt;&lt;a href="#3-%e5%85%b3%e9%97%ad%e4%b8%8d%e5%bf%85%e8%a6%81%e7%9a%84%e9%9d%99%e6%80%81%e8%b5%84%e6%ba%90%e6%9c%8d%e5%8a%a1" class="header-anchor"&gt;&lt;/a&gt;3. 关闭不必要的静态资源服务
&lt;/h3&gt;&lt;p&gt;如果你的 Spring 应用只是一个纯后端 API 服务，建议彻底关闭静态资源服务和相关的缓存配置，以减少攻击面。&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;Spring Framework 7.0.8 的发布不仅是一次常规维护，更是针对 16 项关键 CVE 的集体阻击。在网络安全威胁日益复杂的今天，保持依赖的及时更新是系统最基础的防线。建议所有开发与运维团队尽快排查影响范围并在测试环境中验证升级。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/spring-projects/spring-framework/releases/tag/v7.0.8" target="_blank" rel="noopener"
 &gt;Spring Framework 7.0.8 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://spring.io/security" target="_blank" rel="noopener"
 &gt;Spring 官方安全通告总览&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://spring.io/blog/2026/06/01/spring_and_security_in_the_times_of_ai" target="_blank" rel="noopener"
 &gt;Spring and Security In The Times Of AI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Spring Data 2025.0.13 收官发布：3.5.x 项目升级与迁移检查清单</title><link>https://blog.waihost.com/posts/spring-data-2025-0-13-upgrade-guide/</link><pubDate>Wed, 01 Jul 2026 01:15:51 +0000</pubDate><guid>https://blog.waihost.com/posts/spring-data-2025-0-13-upgrade-guide/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/spring-data-2025-0-13-upgrade-guide.svg" alt="Featured image of post Spring Data 2025.0.13 收官发布：3.5.x 项目升级与迁移检查清单" /&gt;&lt;p&gt;Spring Data 2025.0.13 已经发布。对还在 Spring Boot 3.5.x / Spring Data 3.5.x 线上分支上的团队来说，这不是一个“追新功能”的版本，而更像一次收尾性质的维护窗口：官方发布说明明确提到，2025.0.13 是 Spring Data 3.5.x generation 预期的最后一个开源服务版本，并且只包含回归修复。换句话说，如果你的系统还停留在这一代，短期内应该尽快吸收这个补丁；中期则应该把迁移到 2025.1.x / 4.x 系列纳入排期。&lt;/p&gt;
&lt;p&gt;本文用工程实践的角度梳理：这次发布意味着什么、哪些模块值得重点回归、Maven/Gradle 应该怎么升级，以及团队应该怎样规划下一阶段迁移。&lt;/p&gt;
&lt;h2 id="这次发布的关键信息"&gt;&lt;a href="#%e8%bf%99%e6%ac%a1%e5%8f%91%e5%b8%83%e7%9a%84%e5%85%b3%e9%94%ae%e4%bf%a1%e6%81%af" class="header-anchor"&gt;&lt;/a&gt;这次发布的关键信息
&lt;/h2&gt;&lt;p&gt;先把核心事实讲清楚：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring Data BOM 版本：&lt;code&gt;2025.0.13&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;发布时间：GitHub Release 显示为 &lt;code&gt;2026-06-24T07:40:08Z&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;发布性质：服务版本，主要面向回归修复。&lt;/li&gt;
&lt;li&gt;官方博客说明：它预期是 Spring Data 3.5.x generation 的最后一个开源发布版本。&lt;/li&gt;
&lt;li&gt;官方建议：尽早升级到最新的 4.0.x（&lt;code&gt;2025.1.x&lt;/code&gt; release train）或 4.1.x release。&lt;/li&gt;
&lt;li&gt;Spring 官方博客还说明，Spring Boot 3.5.16 计划在次日拾取这次 Spring Data 发布。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这几个信息放在一起，结论很直接：&lt;code&gt;2025.0.13&lt;/code&gt; 适合作为 Spring Boot 3.5.x 项目的稳定补丁基线，但不应该被当作长期停留点。&lt;/p&gt;
&lt;h2 id="2025013-包含哪些模块"&gt;&lt;a href="#2025013-%e5%8c%85%e5%90%ab%e5%93%aa%e4%ba%9b%e6%a8%a1%e5%9d%97" class="header-anchor"&gt;&lt;/a&gt;2025.0.13 包含哪些模块
&lt;/h2&gt;&lt;p&gt;Spring Data 使用 release train / BOM 管理一组子项目版本。&lt;code&gt;2025.0.13&lt;/code&gt; 对应的参与模块包括：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;模块&lt;/th&gt;
					&lt;th style="text-align: right"&gt;对应版本&lt;/th&gt;
					&lt;th&gt;常见使用场景&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Commons&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;Repository 抽象、分页、排序、映射基础设施&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data JPA&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;JPA / Hibernate 持久化&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data MongoDB&lt;/td&gt;
					&lt;td style="text-align: right"&gt;4.5.13&lt;/td&gt;
					&lt;td&gt;MongoDB 文档数据库访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Redis&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;Redis 数据访问与缓存相关集成&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Cassandra&lt;/td&gt;
					&lt;td style="text-align: right"&gt;4.5.13&lt;/td&gt;
					&lt;td&gt;Cassandra 数据访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Elasticsearch&lt;/td&gt;
					&lt;td style="text-align: right"&gt;5.5.13&lt;/td&gt;
					&lt;td&gt;Elasticsearch 数据访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Couchbase&lt;/td&gt;
					&lt;td style="text-align: right"&gt;5.5.13&lt;/td&gt;
					&lt;td&gt;Couchbase 数据访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Neo4j&lt;/td&gt;
					&lt;td style="text-align: right"&gt;7.5.13&lt;/td&gt;
					&lt;td&gt;图数据库访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data REST&lt;/td&gt;
					&lt;td style="text-align: right"&gt;4.5.13&lt;/td&gt;
					&lt;td&gt;Repository REST 暴露&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Relational&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;JDBC / R2DBC 等关系型访问基础&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data LDAP&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;LDAP 数据访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data KeyValue&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;Key-Value 抽象支持&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;如果你的项目通过 Spring Boot 管理依赖，多数情况下不需要手工声明这些子模块版本；跟随 Spring Boot 3.5.16 或显式导入 &lt;code&gt;spring-data-bom:2025.0.13&lt;/code&gt; 即可。&lt;/p&gt;
&lt;h2 id="值得关注的修复点"&gt;&lt;a href="#%e5%80%bc%e5%be%97%e5%85%b3%e6%b3%a8%e7%9a%84%e4%bf%ae%e5%a4%8d%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;值得关注的修复点
&lt;/h2&gt;&lt;p&gt;这次发布不是大版本功能升级，重点是降低已知回归风险。官方 Release 中比较值得应用开发团队关注的点包括下面几类。&lt;/p&gt;
&lt;h3 id="1-spring-data-commons字段解析相关修复"&gt;&lt;a href="#1-spring-data-commons%e5%ad%97%e6%ae%b5%e8%a7%a3%e6%9e%90%e7%9b%b8%e5%85%b3%e4%bf%ae%e5%a4%8d" class="header-anchor"&gt;&lt;/a&gt;1. Spring Data Commons：字段解析相关修复
&lt;/h3&gt;&lt;p&gt;Spring Data Commons 3.5.13 修复了 &lt;code&gt;TypeDiscoverer&lt;/code&gt; 在子类字段与父类同名字段场景下的解析问题。Release 描述中提到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保留子类字段，而不是被父类中被遮蔽的字段覆盖。&lt;/li&gt;
&lt;li&gt;修复属性类型从隐藏的父类字段解析，而不是从子类字段解析的问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这类问题通常不会影响简单 Entity，但在下面这些场景里更容易踩坑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;领域模型存在继承层次；&lt;/li&gt;
&lt;li&gt;子类重新声明了父类中的同名字段；&lt;/li&gt;
&lt;li&gt;Repository 查询、投影、映射元数据依赖属性类型推断；&lt;/li&gt;
&lt;li&gt;代码生成或历史模型导致字段命名不够干净。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你的项目里有复杂继承模型，升级后建议重点跑 Repository 查询、DTO 投影、Example 查询、排序分页与自定义转换器相关测试。&lt;/p&gt;
&lt;h3 id="2-spring-data-jpajpql--eql-单字符字符串解析修复"&gt;&lt;a href="#2-spring-data-jpajpql--eql-%e5%8d%95%e5%ad%97%e7%ac%a6%e5%ad%97%e7%ac%a6%e4%b8%b2%e8%a7%a3%e6%9e%90%e4%bf%ae%e5%a4%8d" class="header-anchor"&gt;&lt;/a&gt;2. Spring Data JPA：JPQL / EQL 单字符字符串解析修复
&lt;/h3&gt;&lt;p&gt;Spring Data JPA 3.5.13 修复了 JPQL 和 EQL 中集合成员表达式的单字符字符串字面量解析问题。Release 中提到的问题是：单字符字符串字面量作为 &lt;code&gt;MEMBER OF&lt;/code&gt; 左操作数时解析失败。&lt;/p&gt;
&lt;p&gt;受影响的代码可能长得类似下面这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;select u from User u where &amp;#39;A&amp;#39; member of u.flags&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;findUsersWithFlagA&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;实际项目中未必完全使用这个写法，但如果你有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;手写 &lt;code&gt;@Query&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;动态拼接 JPQL / HQL；&lt;/li&gt;
&lt;li&gt;使用集合属性做成员判断；&lt;/li&gt;
&lt;li&gt;查询中存在单字符枚举值或标记值；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;就应该把这些查询纳入回归测试。&lt;/p&gt;
&lt;h3 id="3-spring-data-jpahibernate-依赖升级"&gt;&lt;a href="#3-spring-data-jpahibernate-%e4%be%9d%e8%b5%96%e5%8d%87%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;3. Spring Data JPA：Hibernate 依赖升级
&lt;/h3&gt;&lt;p&gt;Spring Data JPA 3.5.13 的 Release 还列出了 Hibernate 升级到 &lt;code&gt;6.6.53.Final&lt;/code&gt;。这通常是好事，但 ORM 层升级也最容易暴露边界行为差异，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL 生成变化；&lt;/li&gt;
&lt;li&gt;方言兼容性差异；&lt;/li&gt;
&lt;li&gt;延迟加载与关联抓取问题；&lt;/li&gt;
&lt;li&gt;Criteria / JPQL 解析差异；&lt;/li&gt;
&lt;li&gt;数据库驱动与 Hibernate 方言组合问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，JPA 项目升级时不要只跑单元测试，最好至少补一轮连接真实数据库的集成测试。&lt;/p&gt;
&lt;h2 id="maven-项目怎么升级"&gt;&lt;a href="#maven-%e9%a1%b9%e7%9b%ae%e6%80%8e%e4%b9%88%e5%8d%87%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;Maven 项目怎么升级
&lt;/h2&gt;&lt;p&gt;如果你的项目使用 Spring Boot 3.5.x，并通过 &lt;code&gt;spring-boot-starter-parent&lt;/code&gt; 管理版本，推荐优先升级 Spring Boot 到包含该 Spring Data 版本的补丁版本，例如 Spring Boot 3.5.16：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;parent&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-parent&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.5.16&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;relativePath/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/parent&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果项目不能立刻升级 Spring Boot，但确实需要单独锁定 Spring Data release train，可以在 &lt;code&gt;dependencyManagement&lt;/code&gt; 中导入 BOM：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.data&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-data-bom&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2025.0.13&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不过要注意：Spring Boot 自身也会管理 Hibernate、数据库驱动、Spring Framework 等版本。单独覆盖 Spring Data BOM 虽然可行，但更容易形成“局部升级、整体依赖矩阵未验证”的状态。生产项目优先选择 Spring Boot 补丁版本，通常更稳。&lt;/p&gt;
&lt;h2 id="gradle-项目怎么升级"&gt;&lt;a href="#gradle-%e9%a1%b9%e7%9b%ae%e6%80%8e%e4%b9%88%e5%8d%87%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;Gradle 项目怎么升级
&lt;/h2&gt;&lt;p&gt;使用 Spring Boot Gradle 插件的项目，最简单的方式同样是升级 Boot 版本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;org.springframework.boot&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;3.5.16&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;io.spring.dependency-management&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1.1.7&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;java&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果需要显式导入 Spring Data BOM，可以这样写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dependencyManagement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;imports&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mavenBom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;org.springframework.data:spring-data-bom:2025.0.13&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;升级后建议运行依赖树检查，确认最终解析出来的版本符合预期：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./gradlew dependencyInsight --dependency spring-data-commons --configuration runtimeClasspath
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./gradlew dependencyInsight --dependency spring-data-jpa --configuration runtimeClasspath
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./gradlew dependencyInsight --dependency hibernate-core --configuration runtimeClasspath
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Maven 项目可以用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./mvnw dependency:tree -Dincludes&lt;span class="o"&gt;=&lt;/span&gt;org.springframework.data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./mvnw dependency:tree -Dincludes&lt;span class="o"&gt;=&lt;/span&gt;org.hibernate.orm:hibernate-core
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="升级前后的回归检查清单"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%89%8d%e5%90%8e%e7%9a%84%e5%9b%9e%e5%bd%92%e6%a3%80%e6%9f%a5%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;升级前后的回归检查清单
&lt;/h2&gt;&lt;p&gt;建议把这次升级当作一次低风险但必须验证的维护发布。可以按下面顺序做。&lt;/p&gt;
&lt;h3 id="升级前"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%89%8d" class="header-anchor"&gt;&lt;/a&gt;升级前
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;记录当前 Spring Boot、Spring Data、Hibernate、数据库驱动版本。&lt;/li&gt;
&lt;li&gt;确认项目是否手工覆盖过 &lt;code&gt;spring-data-*&lt;/code&gt; 或 Hibernate 版本。&lt;/li&gt;
&lt;li&gt;找出所有自定义 Repository、&lt;code&gt;@Query&lt;/code&gt;、Specification、Querydsl、Criteria 查询。&lt;/li&gt;
&lt;li&gt;标记有继承结构的 Entity / Document / Projection。&lt;/li&gt;
&lt;li&gt;准备真实数据库或测试容器环境，而不是只跑 mock 测试。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="升级后"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%90%8e" class="header-anchor"&gt;&lt;/a&gt;升级后
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;运行完整单元测试与集成测试。&lt;/li&gt;
&lt;li&gt;对核心 Repository 做 CRUD、分页、排序、复杂条件查询回归。&lt;/li&gt;
&lt;li&gt;对 JPA 项目检查启动日志中的 Hibernate 版本与 SQL 方言。&lt;/li&gt;
&lt;li&gt;对 MongoDB / Redis / Elasticsearch 项目检查连接、序列化与索引相关逻辑。&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;dependency:tree&lt;/code&gt; 或 &lt;code&gt;dependencyInsight&lt;/code&gt; 确认没有混入旧版本 Spring Data 模块。&lt;/li&gt;
&lt;li&gt;在预发环境跑一轮真实流量或关键任务。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="为什么不要长期停留在-20250x"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e8%a6%81%e9%95%bf%e6%9c%9f%e5%81%9c%e7%95%99%e5%9c%a8-20250x" class="header-anchor"&gt;&lt;/a&gt;为什么不要长期停留在 2025.0.x
&lt;/h2&gt;&lt;p&gt;官方已经把 2025.0.13 定位为 3.5.x generation 预期的最后一个开源服务版本，这意味着后续开源补丁会转向更新的 release train。对企业项目来说，这通常带来三个影响：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;安全与兼容性窗口变窄&lt;/strong&gt;：依赖链上的修复会优先进入新分支。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;升级跨度变大&lt;/strong&gt;：越晚迁移到 2025.1.x / 4.x，累计变化越多，单次升级风险越高。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生态同步成本上升&lt;/strong&gt;：Spring Boot、Spring Framework、Hibernate、数据库驱动、云原生组件都会继续演进。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此更合理的策略是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;短期：把 3.5.x 项目升级到 &lt;code&gt;2025.0.13&lt;/code&gt; / Boot 3.5.16，拿到最后一批回归修复。&lt;/li&gt;
&lt;li&gt;中期：建立 2025.1.x 或 4.x 的兼容性分支，跑依赖树和自动化测试。&lt;/li&gt;
&lt;li&gt;长期：把主要业务线迁移到新的 Spring Boot / Spring Data 主线，减少历史分支维护成本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="团队落地建议"&gt;&lt;a href="#%e5%9b%a2%e9%98%9f%e8%90%bd%e5%9c%b0%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;团队落地建议
&lt;/h2&gt;&lt;p&gt;如果你负责一个线上 Spring 项目，可以按下面的节奏推进：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;先做补丁升级&lt;/strong&gt;：在当前主线或维护分支升级到 Spring Boot 3.5.16，或至少导入 Spring Data BOM 2025.0.13。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;锁定验证范围&lt;/strong&gt;：重点测试 Repository、JPA 查询、继承模型、MongoDB/Redis 序列化、Elasticsearch 查询。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保留依赖快照&lt;/strong&gt;：把升级前后的依赖树保存到 CI 产物或变更单，方便排查回滚。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;预留迁移分支&lt;/strong&gt;：不要等到必须升级时才看 2025.1.x / 4.x，提前建分支验证编译、启动和关键链路。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免局部硬覆盖&lt;/strong&gt;：除非有明确原因，否则不要长期手工覆盖 Spring Data 子模块版本，让 Spring Boot BOM 统一管理更安全。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="小结"&gt;&lt;a href="#%e5%b0%8f%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;小结
&lt;/h2&gt;&lt;p&gt;Spring Data 2025.0.13 的价值不在于新特性，而在于给 3.5.x generation 一个相对稳妥的收尾版本。对生产系统来说，这类版本往往比“功能发布”更值得及时处理：它能减少已知回归风险，也给后续迁移到 2025.1.x / 4.x 留出缓冲。&lt;/p&gt;
&lt;p&gt;如果你的系统还在 Spring Boot 3.5.x，建议尽快完成补丁升级与回归验证；如果你已经准备进入 Spring Boot 4 / Spring Data 4.x 阶段，则可以把 &lt;code&gt;2025.0.13&lt;/code&gt; 当作维护分支的最后稳定基线，同时把主要精力放到下一代 release train 的兼容性验证上。&lt;/p&gt;</description></item></channel></rss>