MyBatis SqlSession事务与批量处理:默认不生效的真相与正确姿势
我刚开始用MyBatis做数据订正的时候栽过一个很实在的跟头直接用SqlSession循环insert几万条数据跑完了去看库数据确实在心里挺美。结果过了一会儿发现其中一段逻辑在中途抛了异常那批数据只该留着半截的结果全在库里前面已经写进去的根本没有回滚。再后来我想用ExecutorType.BATCH批量提速十万条数据又是差点一条没进去因为循环中间夹了一条select整个批被提前刷掉了更难受的是你还看不出来哪里断了。这两个坑其实是同一类问题在 MyBatis 里用SqlSession管事务、做批量很多“默认”行为和直觉是完全相反的。事务不会因为你调了接口就自动包一层批量也不会因为你写了个for循环就自动攒批。标题那句话说得直白一点就是SqlSession 下的事务和批量不按正确姿势写就默认不生效。这篇文章我就围绕这两个问题展开把SqlSession和事务、执行器之间的关系讲透再给出可直接抄作业的批处理事务代码最后把那些特别容易踩的“看起来生效了、实际没生效”的坑挨个说一下。适合用原生 MyBatis API 做数据同步、批量入库或者想搞懂 Spring 事务底下到底是怎么回事的人。1. 先搞清楚两个“默认不生效”是机制设计不是配置遗漏1.1 SqlSession、Executor、JDBC 事务到底谁在干活很多人把SqlSession当作一个“数据库连接工具”其实它是个门面。真正干活的是内部的Executor而数据库层面的提交、回滚又落在 JDBC 的Connection上。这三层职责拎清楚后面所有问题都顺了。SqlSession对外暴露insert、update、delete、selectOne、selectList、commit、rollback等方法负责把调用转发给 Executor。ExecutorMyBatis 执行器的抽象有SIMPLE、REUSE、BATCH三种实现分别对应普通执行、复用 Statement、批量执行。TransactionMyBatis 自己封装的 JDBC 事务控制持有Connectioncommit和rollback最终都是调connection.commit()和connection.rollback()。SqlSessionFactory.openSession()时你可以指定是否自动提交、事务隔离级别以及使用哪种ExecutorType。这三个参数缺一个没注意后面的行为就歪了。1.2 事务“默认不生效”在原生 API 场景下是什么意思用SqlSessionFactory手工拿SqlSession的时候默认openSession()其实就是openSession(false)也就是autoCommit false。在原生 JDBC 里autoCommit false意味着必须手动commit才会真正提交事务。放到 MyBatis 里也一样。但很多人写代码的时候没有 commit也没有 rollback跑完发现数据居然写进库里了于是以为“MyBatis 默认自动提交”。这通常发生在两种情况下你用的是openSession(true)明确开了自动提交你用的连接池或者某些框架在SqlSession.close()时帮你把连接状态重置了比如连接池在归还连接时把autoCommit改回 true或者某些拦截器顺手提交了。更常见的是你在 Spring 工程里直接用Mapper接口没加TransactionalMyBatis-Spring 的SqlSessionTemplate会在每次 Mapper 的方法调用后自动提交。这个行为造成了一个巨大的错觉MyBatis 好像默认帮你做了事务管理。实际上它提交的是“单个 SQL”这个级别的事务一旦一个循环里前面几条写进去了、后面某一条报错前面那些已经提交的数据是无论如何都回不掉的。原生场景下的“默认不生效”说白了就是事务边界没有被你显式定义MyBatis 不会替你决定从哪一行开始、到哪一行结束。1.3 批量“默认不生效”的真正含义还是原生 API 场景。默认情况下openSession()得到的 Executor 是SIMPLE它每条 SQL 都会创建一个新的Statement、执行一次、关闭。也就是说你在一个for循环里调用mapper.insert(...)一万次底层就是一万次独立的 JDBC 执行和批量没有任何关系。真正的 JDBC 批量应该是Connection conn ...; PreparedStatement ps conn.prepareStatement(insert into user(name) values(?)); for (User user : users) { ps.setString(1, user.getName()); ps.addBatch(); } ps.executeBatch(); conn.commit();addBatch()把参数攒在内存里最后executeBatch()一次性发送给数据库。MyBatis 的ExecutorType.BATCH就是把这个能力封装起来了。问题在于如果你不显式指定它默认根本不开。所以“批量默认不生效”这句话有两层一层是 Executor 默认不是 BATCH另一层是即使你开了 BATCH只要用法不对批也起不来反而可能变成“慢速逐条执行”。2. 正确的打开方式一个真实可用的 SqlSession 批量事务示例2.1 设定目标十万条数据不过量且可回滚我拿一个常见的“导入用户列表”场景举例。假设要从 Excel 或者某个文件里读出 10 万条用户数据插入t_user表要求是不能用SIMPLE执行器逐条 insert否则太慢不能把所有 10 万条攒成一个超级大批次否则内存和数据库报文都可能吃不住控制在“同一批内失败整体回滚已提交的批次不回滚”的粒度。前 10 万条数据无论怎么插失败后都不可能做到整体“全有或全无”因为每批 commit 之后该批事务就已经结束了。所以做这类批量任务要明确区分“批量分组”和“事务边界”一批就是一个独立事务这也是绝大多数批量导入任务的常规设计。2.2 完整代码示例public void batchInsertUsers(ListUser userList) { // 1. 手动打开 SqlSession指定 BATCH 执行器并且关闭自动提交 SqlSession session sqlSessionFactory.openSession(ExecutorType.BATCH, false); int batchSize 500; try { UserMapper mapper session.getMapper(UserMapper.class); for (int i 0; i userList.size(); i) { mapper.insert(userList.get(i)); // 2. 每 500 条手动刷一次批 if (i 0 (i 1) % batchSize 0) { session.flushStatements(); // 3. 刷批之后手动提交事务 session.commit(); } } // 4. 不足一批的剩余部分最后统一刷出并提交 session.flushStatements(); session.commit(); } catch (Exception e) { session.rollback(); throw new RuntimeException(批量插入失败本批次已回滚, e); } finally { session.close(); } }这段代码里的顺序是有讲究的先flushStatements()把 BATCH 执行器里攒的 Statement 真正executeBatch再commit()提交事务。如果你只 commit 不 flushBatchExecutor 的 commit 内部也会触发 flush所以你只写 commit 其实也能刷批。但显式 flush 有两个好处一是代码读起来意图明确二是可以精确控制在某个节点刷新比如为了保证每批 500 条的内存释放时机。2.3 为什么 500 条一个批次内存与事务粒度的平衡批大小这个值并没有放之四海皆准的标准。500 是我在 MySQL 下用得比较多的一个折中值下面讲讲它的依据。数据库方面单条 INSERT 语句的报文大小受max_allowed_packet限制。默认值一般是 4M 或 64M如果一条批量 SQL 拼出来的包太大会直接报PacketTooBigException或者把数据库连接干崩。500 条在普通行宽下基本不会触碰这个限制。客户端方面addBatch攒的是 PreparedStatement 的参数数据量越大JVM 内存和 GC 压力越大。对 10 万条数据来说500 一批会分成 200 个事务既不会让单次事务太长导致锁和 undo 日志膨胀也不会频繁 commit 导致性能明显下降。业务方面如果某批失败最大损失就是 500 条数据的回滚这个量级对业务影响可控。如果你插入的是超宽表几十个字段含大文本或者单条数据量特别大批大小建议下调到 100~200。如果每行数据非常小也可以适当上调。核心思路是批大小不是拍脑袋定的而是由数据库报文限制、内存、事务粒度共同决定的。3. 源码角度什么操作会打断刚搭好的 BATCH3.1 BatchExecutor 的累积与刷新机制BATCH执行器之所以能做到批量是因为它的doUpdate方法里有一套“复用 Statement 累加参数”的逻辑。大意如下如果当前执行的 SQL 和 MappedStatement 和上一次完全一致就把参数继续addBatch到同一个语句上如果换了 SQL 或者换了 MappedStatement就先把旧的 Statement 刷出去再为新的语句创建 Statement 继续攒。这里面最核心的触发点是只要语句发生变化前面攒的批就会被刷掉。所以 BatchExecutor 不是无脑把所有 SQL 攒到最后而是一旦遇到“不一样的语句”立刻会让前面的批生效。这也就是为什么你在批量插入里混入别的 Mapper 的 insert或者混入一条 update批处理的性能会立刻崩掉的原因——它每切换一次语句就执行一次executeBatch实际退化成近乎逐条执行。3.2 批处理会中断的常见“暗雷”第一个暗雷是 select。BatchExecutor 的doQuery里有一个非常容易忽略的细节在真正执行查询之前它会先调用flushStatements()把当前攒着的批全部刷出去。原因是 JDBC 的同一个 Statement 上不能既挂着一堆 addBatch 又去执行查询。所以如果你的批量循环里夹了一条 select那么每次执行到 select 之前批都会被强制 flush。你以为是批量插入实际变成“插入若干条、查一次、插入若干条、查一次”性能甚至比 SIMPLE 执行器还差。第二个暗雷是切换 Mapper 或者切换 SQL。比如你循环里先userMapper.insert再orderMapper.insert那么每处理一段就会触发一次批次切换。不要小看这个行为我在一次真实案例里看到过两套不同的 insert 交替循环 1 万次耗时比正常的SIMPLE还慢原因就在这里。第三个暗雷是连接和事务的运行时长。BatchExecutor 持有的是同一个 PreparedStatement如果一批攒得过大或者数据库端因为有事务长时间不提交导致 undo 膨胀、行锁堆积拖垮的不只是当前任务还可能把同库的其他业务查询拖死。这也是为什么我不建议“攒到结束再一次 flush”的原因而是每 500 条 flush commit。3.3 JDBC 层面还有一个隐藏变量rewriteBatchedStatements这个配置很多 MyBatis 使用者完全没听过但它对 MySQL 批量插入影响极大。MySQL JDBC 驱动默认情况下执行executeBatch时会把每条记录当成一条独立的 INSERT 语句发送只是减少了客户端到服务端的往返次数。真正想要把多条 INSERT 合并成一条多 VALUES 的语句需要在 JDBC URL 上加上rewriteBatchedStatementstrue举个例子插入 500 条用户数据不加这个参数MySQL 会收到 500 条INSERT INTO user(name) VALUES (?)只是协议上打包在一起发送服务端仍然逐条解析执行加了之后驱动会主动把 SQL 改写成INSERT INTO user(name) VALUES (?),(?),(?)...一条大语句带 500 组参数服务端一次解析效率差距非常明显。所以我的建议是在批量场景下JDBC 连接串后面都加上这个参数。不过要注意它也不是万能的。某些特殊 SQL比如带ON DUPLICATE KEY UPDATE或者INSERT ... SELECT的语句驱动可能不会做改写甚至可能失效实际生效与否可以用 MySQL 的通用日志去验证。4. 结合 Spring 使用为什么你会发现连事务都不生效了4.1 MyBatis-Spring 的“每方法一提交”机制如果项目里用了mybatis-spring默认情况下你从 Spring 容器拿到的 Mapper 是被SqlSessionTemplate代理过的。这个类的核心逻辑是每次调用 Mapper 方法时检查当前线程是否已经有 Spring 管理的事务。有就复用同一个 SqlSession没有就新建一个 SqlSession执行完方法后立刻 commit 并关闭。这个机制带来一个非常迷惑人的现象你在 Mapper 的一个方法里比如循环调用 100 次insert执行完后数据全在里你误以为事务自动提交了、自动管理了。实际上这 100 次 insert 是在同一 SqlSession 上执行的但只要没有Transactional每次insert本身不会提交而是在这个 Mapper 方法结束后SqlSessionTemplate 的代理统一 commit 一次。等一下这里说得更准确一点这个“统一”是针对当前任何一次从SqlSessionUtils.getSqlSession获取到 SqlSession 的方法调用周期。如果 100 次 insert 都是同一个循环里的同一个方法内部调用同一个 mapper 方法那确实只会在外层方法结束后提交一次。但你如果是在 Service 层写一个方法里面循环调用userMapper.insert每次调用都会走一次代理每次代理调用结束时没有事务的话就会提交一次。绕开了关键结论是没有 Spring 事务时每次 Mapper 方法调用都是独立提交的之前已提交的数据在后续异常时无法回滚。4.2 Transactional 失效的几类经典场景加了Transactional就一定能回滚吗当然不是下面这几个场景我至少都见过实际案例。同一类内部调用。ClassA的methodA调用了同类里的methodBmethodB上标了Transactional这个事务不会生效。因为 Spring 事务靠代理实现同类内部this.xxx()走的是原始对象不是代理对象。rollbackFor没配。默认情况下Spring 只对RuntimeException和Error回滚。如果你在事务方法里抛了一个自定义的受检异常数据照样提交事务不会回滚。数据库引擎不支持事务。比如 MySQL 的 MyISAM 引擎commit和rollback都是空操作只有改成 InnoDB 才会真正参与事务。多线程调用。事务是绑定在当前线程上的你在事务方法里 new Thread 去执行写操作子线程里没有继承原事务异常了也不会让主线程的事务回滚。事务方法被try-catch吞掉了异常。你捕获了异常并且没往外抛Spring 根本感知不到异常自然也不会回滚。解决思路不复杂确保事务注解加在 public 方法上、通过代理调用、明确rollbackFor、数据库用支持事务的引擎、不要在事务方法里自己消化异常。另外如果排查时拿不准可以把 Spring 的事务日志开起来看TransactionInterceptor是否真的给方法开启了事务。4.3 Spring 项目里真正想用 BATCH 怎么办Spring 项目里的 Mapper 默认是走SqlSessionTemplate如果你希望整个应用都以 BATCH 模式运行可以在配置SqlSessionFactoryBean时把executorType设置成BATCHbean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property nameconfigLocation valueclasspath:mybatis-config.xml/ property nametypeAliasesPackage valuecom.example.entity/ /bean bean idsqlSessionTemplate classorg.mybatis.spring.SqlSessionTemplate constructor-arg index0 refsqlSessionFactory/ constructor-arg index1 valueBATCH/ /bean但需要注意全局BATCH会带来副作用。比如查询操作在 BATCH 模式下也遵循 BatchExecutor 的刷新规则也就是说只要你执行过写操作没 flush再执行查询会先把写操作刷新出去。这在实际业务里会制造很多隐性问题。所以我更推荐的做法是只在真正需要批量的场景里单独拿一个 BATCH 型的 SqlSession 用不要全局开。5. 高频问题与排查速查表我把实际开发里经常被问到的问题整理成了一张表方便你直接对照排查。现象根因解决方案数据插进去了但异常时无法回滚没有显式事务边界MyBatis-Spring 默认每次 Mapper 调用后提交Service 方法加Transactional或用原生 SqlSession 手动 commit/rollback循环 insert 慢得离谱默认 SIMPLE 执行器每条 SQL 独立执行openSession(ExecutorType.BATCH, false)并配合rewriteBatchedStatementstrue开了 BATCH 还是慢循环里混了 select或切换了 Mapper/SQL 导致批次频繁刷新把查询语句移出循环确保同一批处理中只使用同一条 insert 语句批量插入报PacketTooBigException批大小设置过大超过 MySQL 的max_allowed_packet减小批大小或调大max_allowed_packet推荐前者事务注解加了但没效果同类内部调用、受检异常未设置rollbackFor、数据库引擎不支持事务等按 4.2 中的场景逐项排查查询后发现批量数据少了一部分执行 select 时 BatchExecutor 会先 flush但前面可能已经部分提交或有异常检查事务边界和 flush 时机确认各批次提交点批量插入入库后实际没有合并成多 VALUES 语句MySQL 驱动默认不开启 SQL 改写JDBC URL 加rewriteBatchedStatementstrue并验证还有一个很实用的排查技巧怎么确认你的“批量”真的批出去了。最简单的是看日志或加监控。MySQL 可以开general_log执行完批量任务后去日志里看实际下发的 SQL 是 N 条单行 INSERT还是一条多 VALUES 的 INSERT。如果是 N 条单行 INSERT说明rewriteBatchedStatements没生效或者你的语句不支持改写。如果是 MyBatis 的 SQL 日志你也能看到Preparing: insert into ...出现的次数如果每次循环都重新打印 Preparing那基本可以断言批没攒起来。6. 关于批量事务我在实际项目里的一些体感到这里技术点基本都覆盖了。最后想说的是批量事务“默认不生效”并不完全是一件坏事。MyBatis 把事务和批量的开关都设计成“显式开启”反而是为了避免程序员误用一个不可控的隐式机制。如果你愿意把事务边界、批大小、Executor 类型这些变量都抓在自己手里批量任务是可以做得又快又稳的。我自己的习惯是但凡涉及超过几千条的写入一律不依赖 Spring 默认的 Mapper 代理行为而是单独开一个 BATCH 类型的 SqlSession手工控制批量大小和提交点。虽然代码看起来比直接注入UserMapper复杂一点但至少行为是确定的、可预期的。以后你如果再遇到“数据明明写进去了、异常却回滚不了”或者“批量没提速反而更慢”这类问题回头看一下自己是不是也踩了“默认不生效”的坑大概率就能当场定位了。

相关新闻

k8s tips:把 kubeconfig 改到 TaoToken 统一 Key 通道的实操清单

k8s tips:把 kubeconfig 改到 TaoToken 统一 Key 通道的实操清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 20:39:25 阅读更多 →
气密封装代工产线升级 真空甲酸炉采购如何避坑

气密封装代工产线升级 真空甲酸炉采购如何避坑

行业背景:气密封装需求走高,设备采购成为焦点 在5G通信、军用电子、航空航天以及高端传感器等应用领域的推动下,气密封装作为保障芯片长期可靠性的关键工艺,市场需求持续升温。气密封装代工工厂要在气密性等级、良率与交付周期之间…

2026/10/1 20:02:30 阅读更多 →
Spring AI 2.0 + MCP Apps 实战:Java 开发者用 TaoToken 打通多模型调用链路

Spring AI 2.0 + MCP Apps 实战:Java 开发者用 TaoToken 打通多模型调用链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 22:53:20 阅读更多 →

最新新闻

AssetBundle热更新安全排查:从CDN清单到本地缓存的全链路校验指南

AssetBundle热更新安全排查:从CDN清单到本地缓存的全链路校验指南

1. 项目概述:一次热更新安全隐患排查的完整复盘 做 Unity 客户端开发的朋友应该都有体会,AssetBundle 热更新方案上线容易,但真正让它长期稳定跑起来,靠的是细节。尤其是当你的游戏量级上来、CDN 节点分叉、本地缓存策略多样化之后…

2026/10/2 22:53:09 阅读更多 →
Jev浏览器Agent实测:本地部署AI模型驱动浏览器自动化全攻略

Jev浏览器Agent实测:本地部署AI模型驱动浏览器自动化全攻略

最近GitHub上有个叫Jev的浏览器Agent插件火了,21k star,把AI模型和浏览器自动化结合到一起,用自然语言就能驱动浏览器干活。我做了一轮完整的部署和使用测试,从模型选型、本地部署到插件配置、实际跑任务,把整个链路都…

2026/10/2 22:53:09 阅读更多 →
从零搭建AI工程体系:架构设计、核心模块与实操落地指南

从零搭建AI工程体系:架构设计、核心模块与实操落地指南

1. 从零搭建AI工程体系,为什么我劝你别急着调包"ai-engineering-from-scratch"这个标题,第一次看到的时候我愣了一下。市面上讲AI的教程铺天盖地,但绝大多数都是教你pip install一个库,然后调几个API,跑通一…

2026/10/2 22:53:09 阅读更多 →
DeepSeek Harness客户端详解:Token管理与多模型接入实战

DeepSeek Harness客户端详解:Token管理与多模型接入实战

DeepSeek Harness 客户端开放下载,消息一出,不少做 AI 应用开发的朋友都在群里聊这件事。如果你平时经常调 DeepSeek 的 API,或者需要在本地同时管理多个主流大模型的对话与调用,这个客户端确实值得花几分钟试一下。它把模型接入、…

2026/10/2 22:53:09 阅读更多 →
职工考勤管理系统:从数据库设计到状态判定完整实战

职工考勤管理系统:从数据库设计到状态判定完整实战

简介:数据库课程设计——职工考勤管理信息系统完整设计文档,面向计算机相关专业学生及需要完成数据库课程设计的人员。文档以企业考勤管理为背景,系统阐述从需求分析、概念结构设计到逻辑结构设计、物理结构设计与数据库实施的完整流程&#…

2026/10/2 22:53:09 阅读更多 →
互联网商业医疗保险直付平台:从理赔垫付到秒级结算的落地拆解

互联网商业医疗保险直付平台:从理赔垫付到秒级结算的落地拆解

简介:这份PDF文献面向医疗信息化从业者、医院信息中心技术人员及医疗保障研究者,聚焦互联网商业医疗保险直付平台的解决方案。内容系统梳理了商保的概况与现状、传统理赔流程的痛点,并重点论述平台设计原则,包括数据安全、实时性、…

2026/10/2 22:52:08 阅读更多 →

日新闻

从零搭建AI工程化:模型之外的完整闭环

从零搭建AI工程化:模型之外的完整闭环

先搞清楚一件事:从零开始做 AI 工程化,难的从来不是调模型、写提示词,而是把一套原型 Demo 变成长得像是“正经系统”的东西。你手里可能已经有了能跑通的代码,也可能刚读完一些概念,但真到了要把它变成可维护、可观测…

2026/10/2 0:00:20 阅读更多 →
大模型训练显存估计与混合精度训练实战指南

大模型训练显存估计与混合精度训练实战指南

1. 大模型训练显存估计与混合精度训练详解显存不够用,几乎是每个做大模型训练的人都会撞上的第一堵墙。你可能也经历过:模型代码写完了,数据管道跑通了,满心欢喜地按下训练启动脚本,结果几秒钟后终端弹出一行红字——C…

2026/10/2 0:00:20 阅读更多 →
小样本学习数据集选型指南:27个真正可用的高质量数据集

小样本学习数据集选型指南:27个真正可用的高质量数据集

1. 小样本学习的“弹药库”:为什么你总在找数据集,却总找不到真正能用的? 小样本、数据集——这两个词最近半年在我处理的200多个AI项目咨询里,出现频率排进前三。不是模型调不好,不是代码写不对,而是卡在…

2026/10/2 0:00:20 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/1 20:05:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 5:26:06 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 6:09:11 阅读更多 →