Ubuntu 22.04 安装 Apache JDO 3.2.1 与 DataNucleus 指南
在 Ubuntu 22.04 上搭建 Apache JDO 3.2.1 开发环境,以 DataNucleus 6.0.11 为参考实现、H2 为数据库,覆盖坐标核实、pom 集成、字节码增强与 JDOQL 首跑,全部命令附真实回显
Ubuntu 22.04 安装 Apache JDO 3.2.1 与 DataNucleus 指南
JDO(Java Data Objects)是"以纯 Java 对象操作持久化存储"的 JSR 标准(API 3.2.1,包名仍为
javax.jdo,未迁移 jakarta)。它本身只是规范 + API jar,落地需要一个实现,本文采用事实上的参考实现 DataNucleus(core 6.0.11 + api-jdo 6.0.5 + rdbms 6.0.11)。本文所有命令与回显均在 Ubuntu 22.04 容器(Maven 3.9.16 + Temurin 17.0.20.1)中真实执行采集。
一、版本坐标核实(repo1.maven.org metadata 实拍)
| 构件 | 最新版本(metadata <release>) | 说明 |
|---|---|---|
| javax.jdo:jdo-api | 3.2.1 | JDO API,javax.jdo 命名空间 |
| org.datanucleus:datanucleus-core | 6.0.11 | 内核(增强器、元数据、事务) |
| org.datanucleus:datanucleus-api-jdo | 6.0.5 | JDO 规范适配层 |
| org.datanucleus:datanucleus-rdbms | 6.0.11 | 关系型存储支持 |
| org.datanucleus:datanucleus-maven-plugin | 6.0.1 | 构建期字节码增强插件 |
| com.h2database:h2 | 2.2.224 | 演示用嵌入式数据库 |
metadata 实拍(curl https://repo1.maven.org/maven2/javax/jdo/jdo-api/maven-metadata.xml 节选):
<latest>3.2.1</latest>
<release>3.2.1</release>
<version>3.0</version>
<version>3.1-rc1</version>
<version>3.1</version>
<version>3.2</version>
<version>3.2.1</version>
注意 core 与 rdbms 版本号同步(6.0.11),api-jdo 版本线独立(6.0.5),不要写成同一版本号。
二、环境准备
apt-get update && apt-get install -y openjdk-17-jdk-headless maven
或直接使用官方镜像 maven:3.9-eclipse-temurin-17。实测环境回显:
openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment Temurin-17.0.20.1+1 (build 17.0.20.1+1)
Apache Maven 3.9.16
DataNucleus 6.x 要求 JDK 11+;JDK 17 实测全流程正常。
三、工程骨架与 pom 完整坐标
工程位置示例 /work/jdo-demo(容器内实测路径),目录结构:
jdo-demo/
├── pom.xml
└── src/main/
├── java/demo/
│ ├── Person.java
│ └── Main.java
└── resources/META-INF/ # 可选 jdoconfig.xml(本文用编程式 PMF,可省)
pom 关键依赖与插件(完整实测配置):
<dependencies>
<dependency>
<groupId>javax.jdo</groupId>
<artifactId>jdo-api</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-core</artifactId>
<version>6.0.11</version>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-api-jdo</artifactId>
<version>6.0.5</version>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-rdbms</artifactId>
<version>6.0.11</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-maven-plugin</artifactId>
<version>6.0.1</version>
<configuration>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<phase>process-classes</phase>
<goals><goal>enhance</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
四、实体类:Person
package demo;
import javax.jdo.annotations.*;
@PersistenceCapable(table = "PERSONS")
public class Person {
@PrimaryKey
private long id;
private String name;
private int age;
public Person(long id, String name, int age) {
this.id = id; this.name = name; this.age = age;
}
// getter/setter 省略
@Override
public String toString() {
return "Person{id=" + id + ", name='" + name + "', age=" + age + "}";
}
}
五、主程序:编程式 PMF + JDOQL
不使用 jdoconfig.xml,直接用 Properties 创建 PersistenceManagerFactory(减少 XML 失败面,实测通过):
package demo;
import javax.jdo.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
Properties props = new Properties();
props.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"org.datanucleus.api.jdo.JDOPersistenceManagerFactory");
props.setProperty("javax.jdo.option.ConnectionURL",
"jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1");
props.setProperty("datanucleus.schema.autoCreateAll", "true");
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props);
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try {
tx.begin();
pm.makePersistent(new Person(1, "Alice", 30));
pm.makePersistent(new Person(2, "Bob", 22));
pm.makePersistent(new Person(3, "Carol", 27));
tx.commit();
System.out.println("[persist] 3 objects committed");
} finally {
if (tx.isActive()) tx.rollback();
pm.close();
}
// JDOQL 单字符串查询
PersistenceManager pm2 = pmf.getPersistenceManager();
try {
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();
System.out.println("[JDOQL] age > 25 -> " + rows.size() + " rows");
rows.forEach(r -> System.out.println(" " + r));
Person bob = pm2.getObjectById(Person.class, 2L);
System.out.println("[getObjectById] id=2 -> " + bob);
} finally {
pm2.close();
pmf.close();
}
}
}
六、构建与增强:mvn clean package 实测
cd /work/jdo-demo && mvn clean package
enhance 阶段真实输出(注意 [ERROR] 前缀的性质,见第七章):
[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
增强的意义:JDO 运行时需要读写对象字段而不经过 getter,DataNucleus 在构建期把 Person 的字节码改写为实现 javax.jdo.spi.PersistenceCapable(对应日志里的 ENHANCED (Persistable))。跳过增强直接运行会在运行期抛 javax.jdo.JDOFatalUserException: Class ... was not enhanced 类错误——这是 JDO 新手第一大坑。
七、关于日志里的 [ERROR] 前缀(重要排障点)
datanucleus-maven-plugin 6.0.1 会把增强器的 stderr 原样转发到 Maven 日志,因此 INFO 内容也带 [ERROR] 前缀,这不是失败。判定成败看两处:
ENHANCED (Persistable) : <类名>行是否出现;- Maven 最终是否
BUILD SUCCESS。
若想消除误导性前缀,可在插件 <configuration> 中调日志器,或改用独立增强器 CLI(见工具篇第四章)。
八、运行: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
端到端验证链:插入 3 条 → JDOQL 条件查询命中 2 条且按 age 升序(Carol 27 在 Alice 30 前)→ 按主键回读 Bob。数据实际落在 H2 内存库(DB_CLOSE_DELAY=-1 保证 JVM 存活期间可跨 PM 连接)。
九、运行期日志与建表验证说明
实测两点观察:
- DataNucleus 运行期日志(建表 SQL 等)默认不可见——classpath 无 log4j2 绑定时 SQL 日志被吞,
grep CREATE TABLE运行日志为空属正常现象; - 持久化是否成功由查询/回读结果证明即可(第八章输出即证据)。需要看 SQL 时在 Properties 加
datanucleus.query.sql.allowAll=true无关,正确做法是引入log4j-core并把DataNucleus.Datastore级别调到 DEBUG。
十、更换生产数据库(参考)
把 javax.jdo.option.ConnectionURL 换成目标库、补驱动依赖即可(DataNucleus 支持标准 JDBC):
| 数据库 | ConnectionURL 示例 | 额外依赖 |
|---|---|---|
| MySQL | jdbc:mysql://localhost:3306/demo | com.mysql:mysql-connector-j |
| PostgreSQL | jdbc:postgresql://localhost:5432/demo | org.postgresql:postgresql |
| H2(文件) | jdbc:h2:file:./data/demo | 内置 |
| Derby | jdbc:derby:./data/demo;create=true | org.apache.derby:derby |
连接用户名/口令用 javax.jdo.option.ConnectionUserName / ConnectionPassword。
十一、FAQ
Q1:JDO 和 JPA 什么关系? 同为 ORM 持久化标准;JPA 只面向关系库,JDO 还可对接 NoSQL/文件存储(由 DataNucleus 各 store 插件实现)。JPA 实现大多支持 JDO 注解子集,反之不成立。
Q2:为什么是 javax.jdo 而不是 jakarta.jdo?
JDO 3.2.1 仍维护在 javax 命名空间;DataNucleus 6.x 亦以 javax.jdo 为准。jakarta 迁移未在当前版本线发生。
Q3:不增强直接运行会怎样?
运行期抛 JDOFatalUserException(类未实现 PersistenceCapable)。构建期必须经过 enhance。
Q4:能不用 Maven 吗?
能。用 mvn dependency:build-classpath 导出 classpath 后配合独立 Enhancer CLI(工具篇)与普通 javac/java 亦可,但插件方式最省心。
Q5:enhance 输出大量 [ERROR] 但 BUILD SUCCESS,可信吗?
可信(第七章)。[ERROR] 是 stderr 转发产物,判定看 ENHANCED (Persistable) 行。
Q6:JDK 8 能跑 DataNucleus 6.x 吗? 不能,6.x 要求 JDK 11+;老环境需降级 DataNucleus 5.x + JDO 3.2。
Q7:H2 内存库进程结束后数据还在吗?
不在。DB_CLOSE_DELAY=-1 只保证 JVM 生命周期内有效;要持久化改 jdbc:h2:file: URL。
Q8:首次运行很慢? enhance 阶段实测一次 clean package 全量 7 分 29 秒(含依赖下载),二次构建增量走本地仓库明显加快。
Q9:如何查看自动生成的表结构? 连接 H2 内存库需在 JVM 存活期内连接同一 URL;或直接开 DataNucleus SQL DEBUG 日志看 CREATE TABLE。
Q10:JDOQL 支持参数化防注入吗?
支持,q.declareParameters("int minAge") + q.execute(25)(工具篇第六章),生产必须参数化而非字符串拼接。
参考链接
- JDO 规范主页:https://db.apache.org/jdo/
- DataNucleus 文档:https://www.datanucleus.org/products/accessplatform/
- jdo-api 坐标:https://repo1.maven.org/maven2/javax/jdo/jdo-api/