《Java 编程实战》6.1 Jackson 基础:绑定、命名与日期
HTTP 层不能靠字符串拼接生成 JSON。本节用 Jackson 2.22.4 真实演示 record 绑定、命名策略、JavaTimeModule 与 ISO-8601 日期,以及 FAIL_ON_UNKNOWN_PROPERTIES 的报错原文,最后把配置收进 libraryd 的单例 JsonSupport。
tag
HTTP 层不能靠字符串拼接生成 JSON。本节用 Jackson 2.22.4 真实演示 record 绑定、命名策略、JavaTimeModule 与 ISO-8601 日期,以及 FAIL_ON_UNKNOWN_PROPERTIES 的报错原文,最后把配置收进 libraryd 的单例 JsonSupport。
两个借阅请求同时通过「没有活动借阅」检查时,应用层的先查再插并不安全。本节用两个 JDBC 连接真实复现部分唯一索引冲突、条件更新幂等归还与死锁 40P01,并给出 libraryd 借阅、归还和批量写的最终写法,说明为什么 synchronized 在多实例下无效。
两个连接同时读写 loans 时,read committed 与 repeatable read 到底差在哪?本节用真实 PostgreSQL 输出拆开语句快照与事务快照,观察 xmin/xmax 与行锁等待,并给出 libraryd 报表查询与可重试事务的落地写法。
libraryd 的一次借阅要写会员和借阅两条 SQL,JDBC 默认的自动提交会让失败只回滚一半。本节用真实 PostgreSQL 演示 autoCommit=false、commit、rollback 与保存点,把事务边界收在服务方法上,并解释 25P02 与连接归还问题。
列表页、批量导入和借阅详情会把数据访问层的问题放大。本节实现 LIMIT/OFFSET 分页与总数统计,比较逐条插入、addBatch 和 reWriteBatchedInserts 的真实耗时,并用一次 JOIN 消灭活动借阅列表里的 N+1 查询。
小数据量下所有查询都很快,索引问题要等到十万行才暴露。本节用 EXPLAIN (ANALYZE, BUFFERS) 对比 Seq Scan、Index Scan 与 Bitmap Index Scan,给 libraryd 的模糊搜索和活动借阅补上 pg_trgm GIN 索引与部分索引。
数据库 schema 是 libraryd 的最终契约,也是应用层之外的最后一道校验。本节逐条解释 V1__init.sql 里的三张表、主外键、检查约束和部分唯一索引,并用真实插入失败验证每条规则挡住的是什么错误,以及哪些规则不该交给应用层,哪些又必须留给数据库。
SQL 散落在服务层会让事务、错误码和测试都变得难以收口。本节把图书、会员、借阅三类 SQL 收进 JdbcBookRepository、JdbcMemberRepository 与 JdbcLoanRepository,用行映射还原 record,并把 SQLState 翻译成 libraryd 的 ErrorCode。
每条请求都新建物理连接会拖慢 libraryd,也很快耗尽 PostgreSQL 的连接数。本节用 HikariCP 建立有界连接池,讲清 maximumPoolSize、connectionTimeout、maxLifetime 与 leakDetectionThreshold 的含义,并用真实日志定位连接泄漏和池耗尽。
libraryd 从内存仓储走向 PostgreSQL,第一步是把 JDBC 的四个对象用对:DriverManager 与 DataSource 的取舍、PreparedStatement 的参数绑定、ResultSet 的列读取,以及生成键与资源关闭。本节用真实容器跑通图书和借阅记录的写入与查询。
把测试从「跑过」变成「可验收」:用 JaCoCo 的 LINE 与 BRANCH 计数读懂报告,按模块设置 60% 行覆盖率门槛,在 quality profile 里串起编译、测试、Checkstyle、SpotBugs 和覆盖率,最后落到一条 CI 命令。
区分 Checkstyle 管源码风格、SpotBugs 管字节码缺陷:给出一份只保留命名、导入、大括号和魔法数字的精简规则集,用真实违规输出解释阈值与抑制范围,再用 CT_CONSTRUCTOR_THROW 说明 SpotBugs 的第一条告警为什么把异常类声明为 final 就能修复。
把 javac 的 -Xlint 从编辑器提示变成构建门禁:先看 rawtypes 与 unchecked 的真实告警,再用 maven-compiler-plugin 的 -Xlint:all,-processing 加 -Werror,最后给出 libraryd 里处理泛型与受检异常的写法。
让同一 commit 在任何机器上产出相同字节:锁定 Maven 生命周期插件版本,设置 project.build.outputTimestamp,连续两次 clean package 比较 jar 的 SHA-256,并说明 SNAPSHOT、时区与本地缓存会怎样破坏可复现性。
把散落在子模块里的版本号收回父 POM:用 dependencyManagement 与 Jackson BOM 统一版本,用 runtime、test 作用域收紧依赖,再用 Enforcer 的 dependencyConvergence 抓出 Javalin 与 Jedis 带来的真实冲突并收敛。
把卷一的单模块 library 拆成 domain、persistence、web、app、integration-tests 五个模块:先按变化频率和依赖方向划边界,再写出可编译的父 POM、子模块 POM 与最小骨架,最后用 Maven reactor 和依赖树验证拆分结果。