新闻详情

新闻详情

首页 / 资讯中心 / 详情

Spring JDBC分页详解:JdbcTemplate手写分页与深分页优化

发布时间:2026/9/7 3:20:47来源:尧图网络
Spring JDBC分页详解:JdbcTemplate手写分页与深分页优化
Spring JDBC 是 Spring 对 JDBC 的轻量封装它把数据源获取、连接释放、异常翻译、参数绑定和结果集映射都做了统一处理。但只要是写过一段业务代码的人都会发现JdbcTemplate 并没有像 MyBatis-Plus 那样的内置分页插件也没有 PageHelper 那种分页拦截器。要完成分页通常得自己在 SQL 层写 LIMIT、OFFSET或者在 Oracle 里嵌套一层 ROWNUM。这一篇是 SpringJDBC 系列的分页专题会从最简单的 MySQL 分页开始逐步扩展到带搜索条件的分页、跨数据库方言、慢查询排查和索引设计最后给出生产环境里真正可落地的分页方案。适合阅读这篇内容的读者是项目里使用 JdbcTemplate 或 NamedParameterJdbcTemplate但不想为了分页立刻引入 MyBatis-Plus或者已经引入分页插件但遇到“分页失效”“深分页慢”“Oracle 分页丢数据”这类问题想从底层 SQL 角度重新理解原因的人。读完这部分后你应该能独立完成一个带条件过滤、总数统计、多数据库适配的分页查询并能解释为什么有些分页方案在生产环境里会越来越慢。1. Spring JDBC 为什么没把分页封装成现成方法1.1 JdbcTemplate 的职责边界JdbcTemplate 解决的是 JDBC 编码中的重复工作获取连接、创建 Statement、设置参数、遍历 ResultSet、关闭资源、把 SQLException 翻译成 Spring 的 DataAccessException。它并不想接管 SQL 的语义。分页恰好是 SQL 语义中数据库差异最明显的一部分。同一个“取第 2 页的 10 条数据”MySQL 可以写 LIMIT 10 OFFSET 10SQL Server 2012 以后要写 OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLYOracle 11g 及以下则必须借助 ROWNUM 嵌套子查询。JdbcTemplate 如果把这些语法都封装成统一 API要么得内置大量方言判断要么会牺牲掉分页 SQL 的灵活性。所以 Spring JDBC 的选择是做好参数绑定和结果映射把分页 SQL 的拼装交给开发者。这个设计不算缺陷反而让团队能针对自己的数据库和查询场景做精细化优化。1.2 分页在不同数据库中的语法差异在做 Spring JDBC 分页前先要建立一张常用分页方言表数据库核心分页语法使用前提MySQL / PostgreSQLLIMIT ? OFFSET ?直接跟随在 ORDER BY 后面Oracle 11g 及以下通过 ROWNUM 嵌套子查询排序必须放在最内层Oracle 12c 及以上OFFSET ? ROWS FETCH NEXT ? ROWS ONLY需要 ORDER BYSQL Server 2012 及以上OFFSET ? ROWS FETCH NEXT ? ROWS ONLY需要 ORDER BYSQL Server 2008 及以下ROW_NUMBER() OVER (ORDER BY ...)旧版没有 OFFSET FETCH理解这些差异后再写 DAO 就会清晰很多把分页 SQL 的拼装隔离到一个独立策略中而不是在业务代码里到处散落方言判断。1.3 与 MyBatis-Plus 内置分页插件的差别MyBatis-Plus 的分页之所以“看起来不需要写 SQL”是因为它注册了一个拦截器。执行分页查询时拦截器会捕获原始 SQL自动生成 count 查询 SQL再根据配置的数据库方言生成带 LIMIT 或 ROWNUM 的分页 SQL。Spring JDBC 没有这套拦截机制。JdbcTemplate 拿到的 SQL 就是最终执行的 SQL你写什么它执行什么。这意味着总数 SQL 需要你写分页 SQL 需要你写分页参数计算需要你写。但反过来你也能完全控制 SQL 的每一部分。很多团队遇到 MyBatis-Plus 分页失效常查拦截器是否注册、方言是否配置、参数类型是否正确而在 Spring JDBC 中要么 SQL 写对要么写错没有黑盒改写。2. 准备工程、表结构和分页通用模型2.1 通过 Maven 引入依赖这里以 Spring Boot MySQL 为例。Spring Boot 工程里引入 spring-boot-starter-jdbc会自动管理 JdbcTemplate 和数据源相关配置。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency如果项目不是 Spring Boot只需要引入 Spring JDBC 模块dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version5.3.31/version /dependency这段示例中的版本号只用于说明落地时要以你自己工程的 Spring 版本为准。2.2 数据源配置和建表脚本在 Spring Boot 的 application.yml 中配置数据源spring: datasource: url: jdbc:mysql://localhost:3306/dev_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver分页需要一个稳定的测试表。下面建一张用户表字段覆盖常用查询条件用户名、年龄、创建时间。CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, age INT, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP );插入部分测试数据INSERT INTO sys_user(username, age, create_time) VALUES (zhangsan, 25, 2024-01-01 10:00:00), (lisi, 30, 2024-01-02 10:00:00), (wangwu, 35, 2024-01-03 10:00:00);检查点启动工程后能够成功连接数据库查询 sys_user 表能返回数据。这一步没通过后面所有 DAO 测试都会失败。2.3 实体类和分页结果模型分页查询结果建议使用统一的响应模型方便 Controller 返回给前端。前端分页通常需要当前页码、每页条数、列表数据、总记录数、总页数这些字段。先写用户实体public class User { private Long id; private String username; private Integer age; private LocalDateTime createTime; // getter / setter 略 }分页请求参数模型public class PageRequest { private int pageNum 1; private int pageSize 10; public int getOffset() { return (pageNum - 1) * pageSize; } // getter / setter 略 }分页结果模型public class PageResultT { private ListT list; private long total; private int pageNum; private int pageSize; private int totalPages; public static T PageResultT of(ListT list, long total, PageRequest pageRequest) { PageResultT result new PageResult(); result.setList(list); result.setTotal(total); result.setPageNum(pageRequest.getPageNum()); result.setPageSize(pageRequest.getPageSize()); result.setTotalPages((int) ((total pageRequest.getPageSize() - 1) / pageRequest.getPageSize())); return result; } // getter / setter 略 }totalPages 的计算要使用(total pageSize - 1) / pageSize避免整数除法导致最后一页数量不足时少算一页。这个模型在数据库方言切换后不需要变化属于业务层通用对象。3. 用 JdbcTemplate 在 MySQL 上实现第一版分页3.1 核心 SQL 是 ORDER BY LIMIT/OFFSETMySQL 分页最直接的写法SELECT id, username, age, create_time FROM sys_user ORDER BY id DESC LIMIT ? OFFSET ?;这里两个占位符分别是 pageSize 和 offset。offset 表示从第几行开始取计算方式是(pageNum - 1) * pageSize。ORDER BY 不能省略。没有明确排序的分页结果是不稳定的因为数据库不保证返回顺序翻页后可能出现重复或漏数据。实际开发中排序字段一般取唯一字段主键是默认选择。3.2 DAO 实现与参数绑定用 JdbcTemplate 查询列表用 queryForObject 查询总数Repository public class UserDao { private final JdbcTemplate jdbcTemplate; public UserDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate jdbcTemplate; } private final RowMapperUser userRowMapper (rs, rowNum) - { User user new User(); user.setId(rs.getLong(id)); user.setUsername(rs.getString(username)); user.setAge(rs.getInt(age)); user.setCreateTime(rs.getTimestamp(create_time).toLocalDateTime()); return user; }; public PageResultUser pageUsers(PageRequest pageRequest) { String countSql SELECT COUNT(*) FROM sys_user; long total jdbcTemplate.queryForObject(countSql, Long.class); String sql SELECT id, username, age, create_time FROM sys_user ORDER BY id DESC LIMIT ? OFFSET ? ; int offset pageRequest.getOffset(); ListUser users jdbcTemplate.query(sql, userRowMapper, pageRequest.getPageSize(), offset); return PageResult.of(users, total, pageRequest); } }这里 count 返回类型用 Long.class。COUNT(*) 在 MySQL 中返回 BIGINT如果用 Integer.class 接收数据量小的时候可能不报错但存在类型不匹配和溢出风险统一用 Long 更稳。3.3 验证分页结果写一段简单调用代码PageResultUser result userDao.pageUsers(new PageRequest()); System.out.println(total result.getTotal()); System.out.println(totalPages result.getTotalPages()); result.getList().forEach(user - System.out.println(user.getId() user.getUsername()));假如表里有 25 条数据每页 10 条预期第一页输出 10 条用户数据total 为 25totalPages 为 3。如果结果只有 25% 或者重复优先检查 SQL 中的排序字段和 offset 计算。3.4 常见坑LIMIT/OFFSET 语法和排序不稳定MySQL 有两种分页写法LIMIT 10 OFFSET 20和LIMIT 20, 10含义相同但很多人会把LIMIT 20, 10理解成“每页 10 条取第 20 条”实际它是先跳过 20 条再取 10 条。尽量统一使用LIMIT ? OFFSET ?减少歧义。另一个坑是排序字段不唯一。如果只按 create_time 排序而同一时间点有多条记录翻页时数据顺序可能会变化。推荐在排序列后追加主键例如ORDER BY create_time DESC, id DESC同时提醒一点LIMIT 和 OFFSET 应该放在整个 SQL 的最后不能跟在 WHERE 条件之前。4. 带搜索条件的分页让总数和列表复用同一个 WHERE4.1 先定义查询条件对象业务系统里分页很少只是查全表通常带有用户名模糊搜索、年龄区间过滤等条件。新建一个查询对象public class UserQuery { private String username; private Integer minAge; private Integer maxAge; // getter / setter 略 }4.2 用 NamedParameterJdbcTemplate 拼出查询 SQL当条件增多时使用 JdbcTemplate 的?占位符很容易数错位置。推荐改用 NamedParameterJdbcTemplate它使用:username、:minAge这种命名参数。Repository public class UserDao { private final NamedParameterJdbcTemplate namedJdbcTemplate; public UserDao(NamedParameterJdbcTemplate namedJdbcTemplate) { this.namedJdbcTemplate namedJdbcTemplate; } private final RowMapperUser userRowMapper (rs, rowNum) - { // 同上 }; public PageResultUser searchUsers(UserQuery query, PageRequest pageRequest) { MapSqlParameterSource params new MapSqlParameterSource(); String where buildWhere(query, params); String countSql SELECT COUNT(*) FROM sys_user where; long total namedJdbcTemplate.queryForObject(countSql, params, Long.class); params.addValue(limit, pageRequest.getPageSize()); params.addValue(offset, pageRequest.getOffset()); String sql SELECT id, username, age, create_time FROM sys_user where ORDER BY id DESC LIMIT :limit OFFSET :offset ; ListUser users namedJdbcTemplate.query(sql, params, userRowMapper); return PageResult.of(users, total, pageRequest); } private String buildWhere(UserQuery query, MapSqlParameterSource params) { StringBuilder where new StringBuilder( WHERE 1 1); if (query.getUsername() ! null !query.getUsername().isBlank()) { where.append( AND username LIKE :username); params.addValue(username, % query.getUsername() %); } if (query.getMinAge() ! null) { where.append( AND age :minAge); params.addValue(minAge, query.getMinAge()); } if (query.getMaxAge() ! null) { where.append( AND age :maxAge); params.addValue(maxAge, query.getMaxAge()); } return where.toString(); } }4.3 count 查询和 list 查询如何协作总数 SQL 和列表 SQL 都必须使用同一个 buildWhere 方法生成的条件片段。这样做有两个好处过滤逻辑保持唯一不会出现“列表查到了数据总数却对不上”的问题。后续增加过滤字段时只需要改 buildWherecount 和 list 会同步更新。如果条件较少直接在方法内写两份 SQL 也可以。但条件一旦超过三个强烈建议抽取 WHERE 构建方法避免两边忘加条件。4.4 常见坑总数 SQL 不要带 ORDER BY 和 LIMITCOUNT 查询的目的是统计总行数不需要排序也不需要分页。写成这样是错误的SELECT COUNT(*) FROM sys_user WHERE username LIKE %zhang% ORDER BY id LIMIT 10 OFFSET 20COUNT 语句只保留 SELECT 部分和 WHERE 部分即可SELECT COUNT(*) FROM sys_user WHERE username LIKE %zhang%排序和分页只加到列表查询 SQL 中。这样既能减少数据库无谓排序又能避免 COUNT 结果含义错误。5. 跨数据库分页Oracle、SQL Server 和方言抽象5.1 MySQL 与 PostgreSQL 保持一致MySQL 和 PostgreSQL 都支持LIMIT ... OFFSET ...因此上面的代码在 PostgreSQL 上基本不需要改动。要注意的是PostgreSQL 的类型处理比 MySQL 更严格年龄字段如果是 SMALLINT绑定参数时尽量避免传过大的 Integer。5.2 Oracle 分页语法示例Oracle 是分页查询中比较特殊的一类也是开发中踩坑最多的地方。Oracle 11g 及以下版本没有 LIMIT需要使用 ROWNUMSELECT * FROM ( SELECT t.*, ROWNUM AS rn FROM ( SELECT id, username, age, create_time FROM sys_user ORDER BY id DESC ) t WHERE ROWNUM ? ) WHERE rn ?;注意 ROWNUM 是在结果集生成过程中分配的。如果你把 ORDER BY 放在外层ROWNUM 会先于排序分配导致分页结果错乱。正确做法是三层结构最内层负责排序中间层生成 ROWNUM最外层过滤行号。Oracle 12c 及以上版本推荐使用标准 OFFSET FETCHSELECT id, username, age, create_time FROM sys_user ORDER BY id DESC OFFSET ? ROWS FETCH NEXT ? ROWS ONLY;这套语法可读性更高和 SQL Server 2012 后的写法也很接近。5.3 SQL Server 使用 OFFSET FETCHSQL Server 2012 及以上版本的分页写法SELECT id, username, age, create_time FROM sys_user ORDER BY id DESC OFFSET ? ROWS FETCH NEXT ? ROWS ONLY;它与 Oracle 12c 语法几乎一致。唯一容易漏掉的是 OFFSET FETCH 必须配合 ORDER BY否则 SQL Server 会直接报错。5.4 用简单方言策略隔离数据库变化如果项目需要同时兼容多套数据库可以在 DAO 层之前加一个方言接口public interface PaginationDialect { String buildPageSql(String originalSql, int offset, int limit); }MySQL 实现public class MySqlDialect implements PaginationDialect { Override public String buildPageSql(String originalSql, int offset, int limit) { return originalSql LIMIT limit OFFSET offset; } }Oracle 11g 实现public class OracleDialect implements PaginationDialect { Override public String buildPageSql(String originalSql, int offset, int limit) { return SELECT * FROM (SELECT t.*, ROWNUM rn FROM ( originalSql ) t WHERE ROWNUM (offset limit) ) WHERE rn offset; } }这里的 offset 和 limit 是 int 类型由分页参数计算而来不是用户直接输入拼入 SQL 不会引入注入问题。原始 SQL 中仍应使用占位符绑定业务参数。生产中不建议把所有分页 SQL 都固定成同一种方言。更稳妥的方式是在配置中心或系统参数里配置当前数据库类型启动时根据类型选择对应方言实现。个别复杂 SQL 如果 Oracle 嵌套写法不兼容再单独调整。6. 分页慢查询排查、索引设计和深分页处理6.1 为什么 OFFSET 越大越慢很多开发者在数据量小的时候体会不到分页问题等表里积累到百万行后第二天的分页接口开始变慢。原因在于LIMIT 20 OFFSET 100000执行时数据库仍然要先扫描前 100020 行数据然后丢弃前 100000 行只返回最后 20 行。也就是说页码越靠后数据库要做的事情越多响应时间自然越来越长。6.2 用 EXPLAIN 排查索引是否命中遇到分页慢查询第一步不是改代码而是看执行计划。MySQL 中可以这样检查EXPLAIN SELECT id, username, age, create_time FROM sys_user WHERE age BETWEEN 20 AND 30 ORDER BY create_time DESC LIMIT 20 OFFSET 10000;重点看这几个字段字段需要注意的情况type如果看到 ALL说明发生全表扫描possible_keys候选索引是否包含 age 字段key实际使用的索引rows预估扫描行数越大越危险Extra如果看到 Using filesort说明排序没有用到索引如果 WHERE 和 ORDER BY 都命中索引分页性能会明显好很多。但是注意即使走索引深分页依旧要跳过大量索引项性能不会呈线性提升。6.3 深分页改成游标/Keyset 分页当用户只需要“上一页/下一页”不需要跳转到任意页码时可以采用 keyset 分页也叫游标分页。例如按 id 排序的列表第一页查询出最后一条记录的 id 后下一页 SQL 变成SELECT id, username, age, create_time FROM sys_user WHERE id ? ORDER BY id ASC LIMIT ?;这里的?是上一页最后一条记录的 id。数据库不再需要扫描前面已经翻过的数据性能与页数无关。这种方式的限制是不能直接跳页也没法在一开始拿到 total 总数。如果业务上必须显示总页数和跳页功能keyset 分页就不适合。这时可以考虑限制可跳转最大页数比如用户只能查看前 10000 条以内的数据。6.4 常见坑WHERE 和 ORDER BY 字段没有复合索引单独给 age 建索引再让 create_time 排序MySQL 可能无法在一个索引内完成过滤和排序。针对这个查询SELECT id, username, age, create_time FROM sys_user WHERE age BETWEEN 20 AND 30 ORDER BY create_time DESC LIMIT 20;一个比较常见的复合索引设计方向是CREATE INDEX idx_age_create_time ON sys_user(age, create_time);这样 age 用于等值或范围过滤create_time 用于排序。如果要继续回表取得 username 等字段查询计划会做回表操作。若列表查询频繁且性能要求极高可以把 select 列也纳入覆盖索引但这会增大索引占用空间需要权衡。注意索引不是越多越好。每个索引都会拖慢写入和更新生产环境应结合慢查询日志和 EXPLAIN 结果只建立能实际解决的索引。7. Spring JDBC 分页的常见问题、最佳实践和扩展方向7.1 常见问题速查表问题现象可能原因处理建议COUNT 查询报类型转换异常用 Integer.class 接收 BIGINT 结果改为 Long.class第二页和第一页数据重复分页 SQL 缺少 ORDER BY或排序列不唯一统一加 ORDER BY id DESCOracle 分页结果错乱ROWNUM 在排序前生成把排序放入最内层子查询翻页越来越慢OFFSET 过大或索引未命中改用 keyset 分页检查 EXPLAIN搜索条件改了但总数不变count SQL 和 list SQL 的 WHERE 不一致复用同一个 WHERE 构建方法前端页码总是差一页后端 pageNum 从 0 开始前端从 1 开始约定统一从 1 开始MyBatis-Plus 分页失效分页拦截器未注册或方言配置不对先检查拦截器和分页参数传递链7.2 前端页码和后端返回结构要提前约定分页相关接口不只是给后端看前端拿到数据后要显示总页数、当前页和“上一页/下一页”状态。后端响应结构建议固定为{ list: [], total: 100, pageNum: 2, pageSize: 10, totalPages: 10 }这套结构对应前面的 PageResult 模型。前端如果要打印分页数据在每一页上显示“第 2 页 / 共 10 页”只需要读取 pageNum 和 totalPages。如果后端只返回 list前端就没办法准确展示页码。7.3 生产环境落地的几条硬建议分页代码在生产环境要避免“能跑就行”的心态。以下几条可以直接写进团队代码规范pageSize 必须设置上限比如最大 100。否则一次请求可能拉走全表数据拖垮数据库。动态条件统一使用 NamedParameterJdbcTemplate 的命名参数不要拼接用户输入。所有排序字段必须走白名单校验不要允许前端直接传列名到 ORDER BY。count 查询不要带 ORDER BY也不要复用到列表查询的 LIMIT。大表分页优先评估 keyset 分页传统页码分页只保留最近几页。分页查询必须配合适当索引至少覆盖 WHERE 条件列。分页接口要接入慢查询监控观察相同 SQL 在不同数据量下的时间变化。写分页代码时不要只写“成功”路径。要考虑传入 pageNum 或 pageSize 为 0、负数、超大值这些异常情况。PageRequest 构造时可以做一层边界修正public void setPageNum(int pageNum) { this.pageNum Math.max(pageNum, 1); } public void setPageSize(int pageSize) { this.pageSize Math.min(Math.max(pageSize, 1), 100); }7.4 后续可以往哪个方向扩展如果团队觉得手工维护方言和分页 SQL 成本过高可以考虑引入更强封装的数据访问层MyBatis-Plus内置分页插件但要注意拦截器和方言配置Spring Data JPA通过 Pageable 参数快速分页但复杂查询需要接受它的方法命名规则jOOQ对 SQL 方言有完整抽象适合追求 SQL 控制力的团队。选择标准不是“哪个分页插件好用”而是团队对底层 SQL 的掌控能力。Spring JDBC 分页让你亲手写出 LIMIT、COUNT、索引优化这份基本功在迁移到任何 ORM 框架后都能沿用。分页方案没有银弹。数据量不大时最简单的 LIMIT OFFSET 足够数据量大了要么走向 keyset 分页要么基于搜索引擎重新设计查询入口。先把 Spring JDBC 这套手动流程吃透再去看框架封装你会更容易定位“分页失效”到底是在哪一层断掉的。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

磁盘总是不够用?Krokiet 重复文件清理实用指南 2026/9/7 3:47:51

磁盘总是不够用?Krokiet 重复文件清理实用指南

磁盘总是不够用?Krokiet 重复文件清理实用指南 【免费下载链接】czkawka Multi functional app to find duplicates, empty folders, similar images etc. 项目地址: https://gitcode.com/GitHub_Trending/cz/czkawka Krokiet 是一款用 Rust 编写的免费开源清…

阅读更多 →
LobeHub PR 技能详解:canary 分支策略与跨层功能拆分(Stacked PR)实战 2026/9/7 3:47:51

LobeHub PR 技能详解:canary 分支策略与跨层功能拆分(Stacked PR)实战

LobeHub PR 技能详解:canary 分支策略与跨层功能拆分(Stacked PR)实战 【免费下载链接】lobehub 🤯 LobeHub is your Chief Agent Operator, organizing your agents into 724 operations by hiring, scheduling, and reporting o…

阅读更多 →
Unity配置Cursor包:MCP协议打通AI编辑器与场景 2026/9/7 3:47:50

Unity配置Cursor包:MCP协议打通AI编辑器与场景

简介:针对Unity开发者配置AI编程工具Cursor的实用资源,面向需要在Unity编辑器中集成Cursor辅助编码的开发者,解决Package Manager安装与配置相关需求。资源为zip压缩包,共145个文件,以.cs脚本、.meta元数据、.md说明文…

阅读更多 →
JVM程序计数器详解 2026/9/7 3:47:50

JVM程序计数器详解

JVM程序计数器详解 1. 引言 1.1 什么是程序计数器? 程序计数器(Program Counter Register)是JVM内存模型中的一块较小的内存空间。它可以看作是当前线程所执行的字节码的行号指示器。 1.2 程序计数器的位置 程序计数器是线程私有的内存区域&a…

阅读更多 →
JVM垃圾回收机制详解 2026/9/7 3:47:50

JVM垃圾回收机制详解

JVM垃圾回收机制详解 1. 引言 1.1 什么是垃圾回收机制? 垃圾回收(Garbage Collection,GC)是JVM自动管理内存的一种机制。它负责回收不再使用的对象所占用的内存空间,避免内存泄漏,确保程序能够高效运行。 1…

阅读更多 →
rtk 的 Go 生态命令过滤:go test NDJSON 流式解析与 golangci-lint JSON 分组压缩实现 2026/9/7 3:44:50

rtk 的 Go 生态命令过滤:go test NDJSON 流式解析与 golangci-lint JSON 分组压缩实现

rtk 的 Go 生态命令过滤:go test NDJSON 流式解析与 golangci-lint JSON 分组压缩实现 【免费下载链接】rtk CLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies 项目地址: https://gitcode…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞