Java连接openGauss实战:JDBC驱动配置与CRUD避坑指南

发布时间:2026/10/20 8:46:54

Java连接openGauss实战:JDBC驱动配置与CRUD避坑指南 1. 项目缘起与整体设计思路1.1 为什么会有这个需求国产数据库替代这件事这两年在一线开发圈子里讨论得越来越多。我所在的团队去年接手了一个内部管理系统的重构甲方明确要求数据库层面必须用 openGauss应用层继续用 Java 技术栈。当时第一反应是JDBC 连接嘛换个驱动的事。真正动手才发现openGauss 虽然兼容 PostgreSQL 协议但在驱动版本、连接参数、批量操作、事务隔离这些细节上跟平时用 MySQL 的习惯有不少差异踩了一圈坑才跑通。这篇内容就是把这套“Java 连 openGauss 做增删改查”的完整过程整理出来。它解决的核心问题是让一个熟悉 MySQL 或 PostgreSQL 的 Java 开发者能在最短时间内把 openGauss 的 CRUD 跑通并且知道哪些地方容易翻车。适合的人群包括正在做国产化适配的后端开发、需要快速验证 openGauss 可行性的技术负责人、以及想了解国产数据库实际开发体验的学生和爱好者。不管你之前用没用过 openGauss只要会基本的 JDBC 操作这篇都能直接抄作业。1.2 技术选型的几个关键决策动手之前先想清楚用什么方式连。Java 连数据库无非几条路原生 JDBC、MyBatis、JPA/Hibernate、连接池封装。我的建议是先用原生 JDBC 把链路跑通再上框架。原因很简单框架会帮你屏蔽很多细节一旦出问题你根本不知道是驱动的问题、连接串的问题还是框架配置的问题。原生 JDBC 跑通之后你对整个链路的理解是清晰的后面换 MyBatis 还是 JPA 都只是替换 DAO 层的事。驱动方面openGauss 官方提供了opengauss-jdbc这个驱动是基于 PostgreSQL JDBC 驱动改造的所以很多 PostgreSQL 的用法可以直接迁移。但要注意版本匹配驱动版本和数据库服务端版本差太多会出现协议不兼容。我实测下来驱动用 5.x 系列配合 openGauss 5.x 服务端兼容性最稳。连接池这块生产环境肯定不能每次请求都新建连接。HikariCP 是首选轻量、性能好、配置直观。Druid 也可以但它的很多监控功能在 openGauss 上支持得不如 MySQL 那么完善。我个人的选择是 HikariCP配置简单出问题排查也方便。技术选项推荐方案理由连接方式原生 JDBC 起步链路清晰便于排查驱动opengauss-jdbc 5.x与服务端版本匹配兼容性好连接池HikariCP轻量、性能优、配置直观构建工具Maven依赖管理方便社区资料多JDK 版本JDK 8 或 11openGauss 驱动对高版本 JDK 支持需验证1.3 整体架构长什么样整个 demo 的结构非常朴素一个 Maven 项目pom 里引入 openGauss 驱动和 HikariCP然后写一个工具类负责拿连接一个 DAO 类负责增删改查一个主类跑测试。没有花哨的分层目的就是让每一步都看得见。数据库这边先建一张测试表字段设计得稍微全一点包含自增主键、字符串、数字、时间戳、可空字段这样能把常见的类型映射问题都覆盖到。表名我用t_user字段包括id、user_name、age、email、create_time、update_time。这个表结构简单但足够演示所有 CRUD 操作。提示建表时字符集和排序规则要留意。openGauss 默认的字符集和 PostgreSQL 类似如果业务涉及中文建议显式指定兼容的字符集避免出现乱码或者排序不符合预期的情况。2. 环境准备与依赖配置的实操细节2.1 数据库侧的准备工作先确认 openGauss 服务端已经跑起来并且你能用客户端工具连上去。我习惯用官方的 gsql 命令行工具先验证一遍确认服务端没问题再写 Java 代码。这一步很多人会跳过结果 Java 连不上时不知道是服务端的问题还是代码的问题白白浪费时间。建库建表的 SQL 大概长这样CREATE DATABASE demo_db; \c demo_db; CREATE TABLE t_user ( id SERIAL PRIMARY KEY, user_name VARCHAR(64) NOT NULL, age INTEGER, email VARCHAR(128), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP );这里有个细节openGauss 支持SERIAL自增类型用法和 PostgreSQL 一致。但如果你用的是更严格的模式可能需要显式创建序列。我建议初期用SERIAL简单直接。另外要确认数据库的监听地址和端口。默认端口是 5432但很多环境会改。还有pg_hba.conf里的访问控制规则要确保你的客户端 IP 在允许列表里。这个文件改完需要 reload 或者重启服务才生效别改完就急着连先确认配置加载了。2.2 Maven 依赖怎么配pom.xml 里核心就两个依赖openGauss 驱动和 HikariCP。openGauss 的驱动在中央仓库不一定有最新版可能需要配置官方仓库地址或者手动下载 jar 包安装到本地仓库。我这边用的是官方提供的仓库配置方式。dependencies dependency groupIdorg.opengauss/groupId artifactIdopengauss-jdbc/artifactId version5.0.0/version /dependency dependency groupIdcom.zaxxer/groupId artifactIdHikariCP/artifactId version5.0.1/version /dependency dependency groupIdorg.slf4j/groupId artifactIdslf4j-simple/artifactId version2.0.7/version /dependency /dependencies如果中央仓库拉不到 openGauss 驱动有两个办法一是配置官方仓库地址二是去官网下载 jar 包用mvn install:install-file装到本地。我遇到过公司内网无法访问外网仓库的情况就是手动装的命令如下mvn install:install-file \ -Dfileopengauss-jdbc-5.0.0.jar \ -DgroupIdorg.opengauss \ -DartifactIdopengauss-jdbc \ -Dversion5.0.0 \ -Dpackagingjar注意驱动版本不要盲目追新。我试过用最新版驱动连老版本服务端报了一堆协议错误。稳妥的做法是驱动版本和服务端大版本保持一致或者参考官方兼容性矩阵。2.3 连接参数的门道连接串的写法直接决定能不能连上。openGauss 的 JDBC URL 格式和 PostgreSQL 基本一致jdbc:opengauss://host:port/database?param1value1param2value2注意协议头是jdbc:opengauss://而不是jdbc:postgresql://。虽然用 PostgreSQL 的协议头有时候也能连上但官方推荐用 openGauss 自己的协议头避免一些兼容性问题。几个关键参数我列一下参数作用推荐值user数据库用户名按实际配置password密码按实际配置loginTimeout登录超时秒数10connectTimeout连接超时秒数10socketTimeoutsocket 读写超时30prepareThreshold预编译阈值5batchMode批量模式on按需reWriteBatchedInserts重写批量插入trueprepareThreshold这个参数值得说一下。它控制的是同一个 SQL 执行多少次之后转为服务端预编译。默认值是 5意思是执行 5 次后转预编译。预编译能提升性能但也会占用服务端资源。如果你的场景是大量不同的 SQL可以调高这个值甚至设为 0 关闭预编译。reWriteBatchedInserts设为 true 时驱动会把多条 INSERT 重写成一条多值 INSERT批量插入性能提升明显。但这个特性在 openGauss 上的支持情况需要实测我这边测下来是有效的但建议压测确认。3. 核心代码实现与关键环节拆解3.1 连接池的初始化HikariCP 的配置我习惯写在一个单独的类里用静态块初始化全局共用一个 DataSource。这样既避免了重复创建连接池也方便统一管理配置。public class DataSourceHolder { private static final HikariDataSource DATA_SOURCE; static { HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:opengauss://127.0.0.1:5432/demo_db); config.setUsername(your_user); config.setPassword(your_password); config.setDriverClassName(org.opengauss.Driver); config.setMaximumPoolSize(10); config.setMinimumIdle(2); config.setConnectionTimeout(10000); config.setIdleTimeout(600000); config.setMaxLifetime(1800000); config.setConnectionTestQuery(SELECT 1); DATA_SOURCE new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return DATA_SOURCE.getConnection(); } }这里有几个参数需要解释。maximumPoolSize设 10 是保守值生产环境要根据并发量和数据库承载能力调整。connectionTestQuery设成SELECT 1是为了在借出连接前做一次健康检查避免拿到已经失效的连接。openGauss 支持这个查询实测没问题。maxLifetime设 30 分钟比数据库服务端的连接超时时间短一些这样连接池会主动回收老连接避免被服务端强制断开后拿到坏连接。这个值要根据服务端的tcp_keepalives相关配置来定一般设成服务端超时时间的 80% 左右比较稳妥。提示HikariCP 的connectionTestQuery在 JDBC 4 驱动下其实可以省略驱动自带isValid()检测。但 openGauss 驱动在某些版本下isValid()的实现有问题所以我习惯显式配一个测试查询多一层保障。3.2 插入操作的实现与自增主键回填插入是最基础的操作但自增主键的回填是个容易忽略的点。很多业务场景需要在插入后立刻拿到生成的 ID比如关联插入子表。public Long insertUser(String userName, int age, String email) throws SQLException { String sql INSERT INTO t_user (user_name, age, email) VALUES (?, ?, ?); try (Connection conn DataSourceHolder.getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, userName); ps.setInt(2, age); ps.setString(3, email); int affected ps.executeUpdate(); if (affected 0) { throw new SQLException(插入失败没有影响行数); } try (ResultSet rs ps.getGeneratedKeys()) { if (rs.next()) { return rs.getLong(1); } } return null; } }关键点在prepareStatement的第二个参数Statement.RETURN_GENERATED_KEYS。这个参数告诉驱动要返回生成的主键。openGauss 驱动对这个参数的支持是有的但要注意如果表的主键不是自增类型或者用了自定义序列返回的 key 可能不符合预期。我实测SERIAL类型是能正确返回的。还有一个坑批量插入时getGeneratedKeys()的行为可能和单条插入不一样。有些驱动版本在批量模式下只返回最后一条的 key或者返回的 key 顺序和插入顺序不一致。如果你的业务依赖批量插入后的主键建议先小批量测试确认行为。3.3 查询操作的映射与分页查询的难点不在 SQL 本身而在结果集到对象的映射以及分页的处理。openGauss 支持标准的LIMIT和OFFSET语法分页写起来和 PostgreSQL 一样。public ListUser queryUsers(int page, int pageSize) throws SQLException { String sql SELECT id, user_name, age, email, create_time, update_time FROM t_user ORDER BY id LIMIT ? OFFSET ?; ListUser users new ArrayList(); try (Connection conn DataSourceHolder.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, pageSize); ps.setInt(2, (page - 1) * pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { User user new User(); user.setId(rs.getLong(id)); user.setUserName(rs.getString(user_name)); user.setAge(rs.getInt(age)); user.setEmail(rs.getString(email)); user.setCreateTime(rs.getTimestamp(create_time)); user.setUpdateTime(rs.getTimestamp(update_time)); users.add(user); } } } return users; }这里有个性能细节OFFSET分页在数据量大时性能会急剧下降因为数据库需要扫描并跳过前面的行。如果表数据量超过百万级建议用游标分页也就是基于上一页最后一条记录的 ID 来查下一页SELECT * FROM t_user WHERE id ? ORDER BY id LIMIT ?这种方式不管翻到第几页性能都是稳定的。缺点是只能顺序翻页不能跳页。具体用哪种看业务需求。类型映射方面openGauss 的TIMESTAMP对应 Java 的java.sql.TimestampVARCHAR对应StringINTEGER对应int或Integer。要注意INTEGER字段如果可能为 NULL必须用包装类型Integer否则rs.getInt()返回 0 会掩盖 NULL 的真实语义。3.4 更新与删除的注意事项更新和删除的代码结构类似都是执行executeUpdate然后检查影响行数。这里重点说几个容易出问题的地方。public boolean updateUser(Long id, String userName, Integer age, String email) throws SQLException { String sql UPDATE t_user SET user_name ?, age ?, email ?, update_time CURRENT_TIMESTAMP WHERE id ?; try (Connection conn DataSourceHolder.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, userName); if (age null) { ps.setNull(2, Types.INTEGER); } else { ps.setInt(2, age); } ps.setString(3, email); ps.setLong(4, id); return ps.executeUpdate() 0; } }第一个注意点是 NULL 值的处理。ps.setInt()不能传 null必须用ps.setNull(index, Types.INTEGER)。这个在动态更新场景下特别容易忘导致空指针异常。第二个注意点是更新时的并发控制。如果两个线程同时更新同一条记录后提交的会覆盖先提交的。要避免这种情况可以用乐观锁在表里加一个version字段更新时带上版本号条件UPDATE t_user SET user_name ?, version version 1 WHERE id ? AND version ?如果影响行数为 0说明版本号不匹配记录已被别人改过业务层需要处理这种冲突。删除操作相对简单但要注意外键约束。如果表之间有外键关联删除主表记录时如果子表有引用会报约束冲突。这时候要么先删子表要么用级联删除。openGauss 支持ON DELETE CASCADE建表时指定即可。注意生产环境做删除操作我强烈建议用逻辑删除而不是物理删除。加一个is_deleted字段删除时更新这个字段而不是真的删行。这样数据可恢复也避免了外键约束的麻烦。物理删除只在数据清理等特定场景用。4. 常见问题排查与避坑经验实录4.1 连接不上数据库的排查思路连不上是最常见的问题原因可能出在网络、认证、配置各个环节。我整理了一个排查顺序按这个顺序走基本能定位到问题。排查步骤检查内容常见问题1服务端是否启动进程不在端口未监听2网络是否通防火墙拦截安全组未放行3端口是否正确默认 5432 被改过4认证配置pg_hba.conf 未允许客户端 IP5用户名密码密码含特殊字符未转义6驱动版本与服务端版本不兼容7连接串格式协议头写错参数拼写错误我遇到最多的是第 4 步pg_hba.conf里默认只允许本地连接远程连接需要加一行配置。改完记得 reload。还有一次是密码里有符号在连接串里没转义导致解析出错。密码里的特殊字符要么 URL 编码要么用 Properties 对象传参不要直接拼在 URL 里。4.2 中文乱码与字符集问题中文乱码的根源是客户端和服务端字符集不一致。openGauss 服务端建库时可以指定字符集客户端连接时驱动也有字符集设置。两边要匹配。排查方法先查服务端的字符集SHOW server_encoding;然后在 Java 侧确认连接的字符集。可以在连接串里加characterEncodingUTF-8或者通过conn.getClientInfo()查看。如果服务端是 UTF-8客户端也设 UTF-8一般不会乱码。还有一种情况是数据写入时没问题读取时乱码。这通常是 JDBC 驱动读取时的编码转换问题。可以尝试在连接串里显式指定characterEncoding或者在 JVM 启动参数里加-Dfile.encodingUTF-8。4.3 批量操作的性能问题批量插入如果一条一条执行性能会很差。正确的做法是用addBatch()和executeBatch()public void batchInsert(ListUser users) throws SQLException { String sql INSERT INTO t_user (user_name, age, email) VALUES (?, ?, ?); try (Connection conn DataSourceHolder.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { conn.setAutoCommit(false); for (User user : users) { ps.setString(1, user.getUserName()); ps.setInt(2, user.getAge()); ps.setString(3, user.getEmail()); ps.addBatch(); } ps.executeBatch(); conn.commit(); } }几个性能要点一是关闭自动提交手动控制事务减少提交次数二是批量大小要合适太小没效果太大占内存我一般设 500 到 1000 条一批三是配合前面提到的reWriteBatchedInsertstrue参数让驱动帮你优化 SQL。实测下来批量插入 1 万条数据逐条插入要几十秒用批量加事务能降到一两秒。差距非常明显。提示批量操作时如果中途出错已经执行的部分可能已经生效。要么整个批次回滚要么用 savepoint 做部分回滚。这个要根据业务对一致性的要求来决定。4.4 事务隔离与锁等待openGauss 默认的事务隔离级别是读已提交Read Committed和 PostgreSQL 一致。大部分业务场景够用。但如果你的业务需要可重复读可以显式设置conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);要注意的是隔离级别越高锁的粒度越大并发性能越差。而且 openGauss 的可重复读实现和 MySQL 不一样不会像 MySQL 那样用间隙锁所以幻读的防护机制不同。如果你的业务逻辑依赖 MySQL 的锁行为迁移到 openGauss 时需要重新验证。锁等待超时也是常见问题。一个事务持有锁太久另一个事务等锁超时就会报错。可以通过设置lock_timeout参数来控制等待时间。排查锁等待可以用系统视图查当前锁的情况找到阻塞的源头。4.5 驱动版本与服务端版本不匹配的坑这个坑我踩得最深。当时用了一个比较新的驱动版本连老版本服务端报了一堆看不懂的协议错误。后来查文档才发现openGauss 的驱动和服务端之间有兼容性矩阵不是随便配都行的。我的建议是驱动版本和服务端大版本保持一致小版本可以略有差异。比如服务端是 5.0.x驱动就用 5.0.x。如果实在不确定用官方文档推荐的组合。升级驱动前先在测试环境验证不要直接上生产。还有一个细节openGauss 驱动和 PostgreSQL 驱动虽然同源但不能混用。有些项目里同时引入了两个驱动导致类冲突。检查一下依赖树确保只有 openGauss 驱动。5. 从能跑到好用进阶优化建议5.1 SQL 预编译与执行计划缓存前面提到prepareThreshold参数控制预编译的时机。预编译的好处是 SQL 解析一次后续执行复用执行计划减少解析开销。但 openGauss 的执行计划缓存机制和 PostgreSQL 类似对于数据分布变化大的表缓存的计划可能不是最优的。我的经验是对于频繁执行且参数变化不大的 SQL开启预编译收益明显对于偶尔执行或者参数差异极大的 SQL关闭预编译反而更好。可以通过prepareThreshold0关闭预编译或者设一个较大的值让它很少触发。另外openGauss 支持EXPLAIN查看执行计划遇到慢查询时先用EXPLAIN ANALYZE看看瓶颈在哪是索引没走还是 join 顺序不对。这个和 PostgreSQL 的用法完全一样。5.2 连接泄漏的检测与预防连接泄漏是生产环境的隐形杀手。连接池的连接被借出后没归还池子里的连接越来越少最后请求全部阻塞。HikariCP 提供了泄漏检测功能config.setLeakDetectionThreshold(60000);设成 60000 毫秒意思是连接借出超过 60 秒没归还就打印警告日志。这个值不要设太小否则正常的慢查询也会触发警告。我一般设 60 秒到 120 秒之间。预防连接泄漏的根本方法是确保每个Connection、PreparedStatement、ResultSet都在 try-with-resources 里创建这样不管有没有异常都会自动关闭。我 review 代码时看到手动close()的写法就会特别留意因为一旦中间抛异常close 就可能被跳过。5.3 读写分离与负载均衡的考虑如果业务读多写少可以考虑读写分离。openGauss 支持主备部署JDBC 驱动可以配置多个节点实现读请求分发到备节点。连接串里可以写多个 hostjdbc:opengauss://host1:5432,host2:5432/demo_db?targetServerTypepreferSecondarytargetServerType参数控制连接的目标节点类型。preferSecondary表示优先连备节点适合只读场景。写操作则用master或者preferPrimary。不过读写分离会带来主备延迟的问题。刚写入的数据可能在主节点备节点还没同步读的时候读不到。对一致性要求高的场景要谨慎使用或者把关键读操作强制走主节点。5.4 监控与日志的接入生产环境一定要有监控。HikariCP 暴露了很多 JMX 指标可以接入监控系统看连接池的活跃连接数、空闲连接数、等待线程数。这些指标能提前发现连接池配置不合理的问题。日志方面openGauss 驱动支持通过 SLF4J 输出日志。把日志级别调到 DEBUG 可以看到驱动执行的 SQL 和参数排查问题时非常有用。但生产环境不要开 DEBUG日志量太大会影响性能。我一般用 INFO 级别只在排查特定问题时临时调低。提示驱动日志里可能会打印 SQL 参数如果参数包含敏感信息比如密码、身份证号要注意日志脱敏。这个在合规要求高的场景下尤其重要。6. 一些个人体会这套东西跑下来最大的感受是openGauss 在 JDBC 层面的使用体验和 PostgreSQL 非常接近有 PostgreSQL 经验的开发者上手成本很低。真正的坑集中在版本兼容、连接参数配置、以及一些边界场景的行为差异上。这些坑不难解决但不知道的话会浪费很多时间。我现在做国产数据库适配习惯先建一个最小可运行的 demo把连接、增删改查、事务、批量这几个基础能力跑通再往框架里集成。这样出问题时排查范围小定位快。直接上框架看着省事实际上是把问题藏起来了后面爆发出来更难处理。另外驱动版本和服务端版本的匹配这件事值得单独花时间确认。不要想当然地认为新版本一定兼容老版本数据库驱动这块版本匹配比版本新更重要。
延伸阅读

更多相关文章

2026/10/20 8:46:54

CMake 3.14.2 win64-x64搭建OpenCV环境:从安装到避坑

简介:面向Windows 64位开发者的CMake 3.14.2官方发行版,用于在跨平台场景下统一管理构建过程,生成Visual Studio解决方案或makefile。该版本对OpenCV这类依赖较多第三方库的项目尤其友好,可在CMakeLists.txt中集中指定头文件、库路…

2026/10/20 8:41:53

Vivado HLS设计流程核心:调度、接口与优化实战总结

做了快两年的Vivado HLS开发,踩过的坑比看过的教程还多。前阵子有个同事看我用C写出来的模块能直接生成RTL,觉得特别神奇,问我这个工具到底靠不靠谱、上手有什么门道。我想了想,与其每次零零散散地跟他讲,不如把这些经…

2026/10/20 9:57:25

天工Skywork桌面版部署指南:从源码包到可运行环境

简介:这份资源面向希望零代码上手国产桌面AI代理工具的办公人士与知识管理者,提供天工Skywork桌面版的完整部署指南与可运行源码,帮助非技术用户绕开WSL2,在Windows环境下完成原生部署。压缩包共3个文件,约4KB&#xf…

2026/10/20 9:57:25

10亿美元融资与三顾茅庐:AI泰斗押注世界模型与自监督学习

这事刚出的时候,我的几个技术交流群一下午都没消停。一位在卷积神经网络时代就封神的泰斗级学者A,从老东家那边转身离场,又跑到一位视觉表征领域青年学者B的门下反复游说,最终把这位长年在自监督学习一线深耕的华人学者拉进了自己…

2026/10/20 9:57:25

C#抓包实战:SharpPcap与PacketDotNet解析数据包

简介:面向C#网络编程学习者与开发者的数据包捕获解析完整项目,基于Socket编程实现指定端口的IP、TCP、UDP报文监听与解析,可辅助网络排错、协议学习和安全分析。压缩包共82个文件、约1.12MB,以24个C#源代码和3个可执行程序为主体&…

2026/10/20 9:57:25

基于SSM+Vue的天气查询毕设:从选题架构到部署上线的完整实战

又一个毕设季,后台经常有人问我:“不想做商城系统,也不想做博客了,有没有一个题目既不像玩具、又不会把自己写崩?”我的回答通常是同一个:天气查询。别小看这个小题目,它几乎没有支付、权限、并…

2026/10/20 9:52:24

Android USB串口打印热敏小票的底层通信与ESC/POS实战

1. 这不是“接个打印机”那么简单:Android USB串口打印的本质挑战很多人看到“Android USB串口打印结账单小票”这个标题,第一反应是:“不就是连个热敏打印机,发几行文本过去吗?网上一搜全是教程。”我最初也是这么想的…

2026/10/19 18:06:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/20 1:05:37

单片机C运行时从启动到多任务:中断安全与共享资源冲突

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

2026/10/20 1:05:37

GCC编译四步详解:预处理、编译、汇编、链接实战

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

2026/10/20 1:05:37

FIW线是什么?FEP注塑线与普通氟塑料线的区别及应用指南

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

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑