Apache JDO 3.2.1 常用命令与工具详解
详解 Apache JDO 3.2.1 开发工具链:DataNucleus 字节码增强(maven-plugin 实测与独立 CLI 参考)、exec-maven-plugin 运行、JDOQL 查询实战与 mvn 命令速查,全部实测输出照抄
Apache JDO 3.2.1 常用命令与工具详解
JDO 3.2.1 是规范(API jar),命令行工具由实现 DataNucleus 提供。本文实测环境:DataNucleus 6.0.11 / api-jdo 6.0.5 / maven-plugin 6.0.1 / H2 2.2.224,Temurin 17.0.20.1 + Maven 3.9.16(Ubuntu 22.04 容器)。标注"实测"的输出均为真实回显;标注"参考"的用法来自官方文档、未在本次实验执行。
一、工具链总览
| 工具 | 阶段 | 用途 | 本文状态 |
|---|---|---|---|
datanucleus-maven-plugin enhance | 构建期 | 字节码增强 → PersistenceCapable | 实测 |
| DataNucleus Enhancer CLI | 构建期 | 不用 Maven 时的独立增强 | 参考 |
exec-maven-plugin java | 运行期 | 以工程 classpath 跑主类 | 实测 |
| JDOQL(API) | 运行期 | 对象查询语言 | 实测 |
| JDOQLTypedQuery | 运行期 | 类型安全查询(编译期校验) | 参考 |
| mvn dependency:build-classpath | 辅助 | 导出 classpath 供 javac/java 用 | 实测(命令有效) |
二、字节码增强:为什么必须做
JDO 运行时要在 getter/setter 之外直接读写对象字段并感知脏状态,因此 DataNucleus 在构建期把 @PersistenceCapable 类改写为实现 javax.jdo.spi.PersistenceCapable。跳过增强直接运行,会在获取 PM 时抛出"类未增强"的 JDOFatalUserException——这是 JDO 最常见的启动失败。
三、datanucleus-maven-plugin enhance 实测
pom 绑定(见安装指南篇第三章)后执行:
cd /work/jdo-demo && mvn clean package
真实输出(增强段完整照抄):
[INFO] DataNucleus Enhancer (version 6.0.11) for API "JDO"
ENHANCED (Persistable) : demo.Person
DataNucleus Enhancer completed with success for 1 classes. Timings : input=69 ms, enhance=56 ms, total=125 ms. Consult the log for full details
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 07:29 min
判定成败的正确姿势:插件把增强器 stderr 转发为 [ERROR] 前缀行,内容实为 INFO,不是失败。只认两处:ENHANCED (Persistable) : <类> 出现 + BUILD SUCCESS。
验证增强产物(可选):
javap -p -c target/classes/demo/Person.class | grep -i persistable | head -3
增强后的类会实现 javax.jdo.spi.PersistenceCapable 接口并带 dnGet*/dnSet* 系列方法(javap 输出可见 dnGetname、jdoIsDirty 等)。
四、独立 Enhancer CLI(参考用法,本次未实测)
没有 Maven 的环境(如手工 javac 工程)用独立增强器:
CP="datanucleus-core-6.0.11.jar:datanucleus-api-jdo-6.0.5.jar:jdo-api-3.2.1.jar"
java -cp "$CP" org.datanucleus.enhancer.DataNucleusEnhancer -api JDO -v target/classes/demo/Person.class
-api JDO指定规范层;-v等效 verbose;- 入参可以是 class 文件、目录或 class 名列表;
- 依赖 jar 可用
mvn dependency:build-classpath -Dmdep.outputFile=cp.txt导出后拼接。
成功判据与插件一致:输出 ENHANCED (Persistable) 行。若用此方式,注意先编译(javac)再增强,最后运行的 classes 目录就是增强后的字节码。
五、运行工程:exec-maven-plugin 实测
mvn org.codehaus.mojo:exec-maven-plugin:3.1.0:java -Dexec.mainClass=demo.Main
真实输出(完整照抄):
[persist] 3 objects committed
[JDOQL] age > 25 -> 2 rows
Person{id=3, name='Carol', age=27}
Person{id=1, name='Alice', age=30}
[getObjectById] id=2 -> Person{id=2, name='Bob', age=22}
[INFO] BUILD SUCCESS
端到端链路解读:
makePersistent×3 提交 3 条(实测输出[persist] 3 objects committed);- JDOQL
SELECT FROM demo.Person WHERE age > 25 ORDER BY age ASC命中 2 行,Carol(27) 排在 Alice(30) 前——ORDER BY 生效证据; getObjectById(Person.class, 2L)按主键直读 Bob(22),未触发集合扫描。
六、JDOQL 实战速查
6.1 单字符串形式(实测)
Query<Person> q = pm2.newQuery("javax.jdo.query.JDOQL",
"SELECT FROM demo.Person WHERE age > 25 ORDER BY age ASC");
List<Person> rows = (List<Person>) q.execute();
6.2 常用子句对照
| 需求 | JDOQL 写法 |
|---|---|
| 条件 | WHERE age > 25 && name.startsWith('A') |
| 参数 | PARAMETERS int minAge + WHERE age > minAge + q.execute(25) |
| 变量 | VARIABLES String x(集合遍历场景) |
| 排序 | ORDER BY age ASC, name DESC |
| 去重 | SELECT DISTINCT FROM ... |
| 聚合 | SELECT COUNT(this) FROM ... / GROUP BY + HAVING |
| 分页 | q.setRange(10, 20)(API 方法,非字符串子句) |
| 原生 SQL | pm.newQuery("javax.jdo.query.SQL", "SELECT ...") |
6.3 单对象读取(实测)
Person bob = pm2.getObjectById(Person.class, 2L);
主键类型须与 @PrimaryKey 字段一致(实测 long 主键传 2L)。
七、mvn 命令速查
| 命令 | 作用 | 备注 |
|---|---|---|
mvn clean package | 编译+增强+打包 | 增强绑定在 process-classes,天然包含 |
mvn org.codehaus.mojo:exec-maven-plugin:3.1.0:java -Dexec.mainClass=demo.Main | 跑主类 | 实测命令 |
mvn dependency:build-classpath -Dmdep.outputFile=cp.txt | 导出 classpath | 供独立增强器/手工 java 用 |
mvn -o clean package | 离线构建 | 依赖齐备后可脱离仓库 |
mvn test -Dtest=PersonTest | 单测 | 增强同样会执行(process-classes 先于 test) |
八、运行期日志与 SQL 可观测性
实测发现:classpath 无 log4j2 绑定时,DataNucleus 的建表 SQL、连接日志默认全部静默(grep CREATE TABLE 运行日志为空)。观测建议:
- 引入
log4j-core+ log4j2.xml,把DataNucleus.Datastore调到 DEBUG 可看到每条 SQL; - 功能正确性优先用查询/回读结果证明(第五章输出即端到端证据),日志只是辅助;
- H2 内存库排查时保持
DB_CLOSE_DELAY=-1,否则连接断开即清库。
九、FAQ
Q1:Class demo.Person was not enhanced 怎么排查?
确认 plugin 绑定 phase=process-classes 且 mvn clean package 后运行的是 target/classes 产物;跳过 package 直接 IDE 运行最容易踩。
Q2:增强器把哪些类增强了?
只增强 @PersistenceCapable(或按 includes 配置)的类;构建日志逐类打印 ENHANCED (Persistable)。
Q3:多模块工程每个模块都要配插件吗? 实体类所在模块必须配;只引用实体的模块不需要。
Q4:能对已编译 jar 里的类增强吗? 独立 Enhancer CLI 可对解包后的 class 文件操作,但更推荐拿源码重新构建。
Q5:exec:java 与 java -jar 区别? exec:java 用当前工程完整 classpath(含依赖)在原 JVM 内运行;java -jar 需要 fat jar。JDO 工程用 exec:java 最省事。
Q6:JDOQL 里字符串日期怎么比?
用参数化 PARAMETERS java.util.Date d 传 Date 对象,避免字符串格式歧义。
Q7:怎么打印最终执行的 SQL?
DataNucleus.Datastore DEBUG 日志(见第八章),或 H2 自带 ;TRACE_LEVEL_SYSTEM_OUT=2 URL 参数。
Q8:getObjectById 传 Integer 而主键是 long?
实测语义上需匹配类型,用 2L;类型不符可能查不到或抛异常。
Q9:JDOQLTypedQuery 什么时候用? 大团队/重编译校验场景,查询写成 Java 表达式编译期报错;轻量项目单字符串 JDOQL 足够。
Q10:增强后的类还能序列化/反射吗?
可以,增强只加方法不改既有签名;注意 jdoCopyFields 等新方法出现在反射列表属正常。
参考链接
- DataNucleus Enhancer 文档:https://www.datanucleus.org/products/accessplatform/jdo/enhancer.html
- JDOQL 官方参考:https://db.apache.org/jdo/jdoql.html
- datanucleus-maven-plugin:https://repo1.maven.org/maven2/org/datanucleus/datanucleus-maven-plugin/