Ubuntu 22.04 安装 Apache JDO 3.2.1 与 DataNucleus 指南

在 Ubuntu 22.04 上搭建 Apache JDO 3.2.1 开发环境,以 DataNucleus 6.0.11 为参考实现、H2 为数据库,覆盖坐标核实、pom 集成、字节码增强与 JDOQL 首跑,全部命令附真实回显

Apache JDOJDOApacheJava安装部署Ubuntu 22.042026/10/15

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-api3.2.1JDO API,javax.jdo 命名空间
org.datanucleus:datanucleus-core6.0.11内核(增强器、元数据、事务)
org.datanucleus:datanucleus-api-jdo6.0.5JDO 规范适配层
org.datanucleus:datanucleus-rdbms6.0.11关系型存储支持
org.datanucleus:datanucleus-maven-plugin6.0.1构建期字节码增强插件
com.h2database:h22.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] 前缀,这不是失败。判定成败看两处:

  1. ENHANCED (Persistable) : <类名> 行是否出现;
  2. 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 连接)。

九、运行期日志与建表验证说明

实测两点观察:

  1. DataNucleus 运行期日志(建表 SQL 等)默认不可见——classpath 无 log4j2 绑定时 SQL 日志被吞,grep CREATE TABLE 运行日志为空属正常现象;
  2. 持久化是否成功由查询/回读结果证明即可(第八章输出即证据)。需要看 SQL 时在 Properties 加 datanucleus.query.sql.allowAll=true 无关,正确做法是引入 log4j-core 并把 DataNucleus.Datastore 级别调到 DEBUG。

十、更换生产数据库(参考)

把 javax.jdo.option.ConnectionURL 换成目标库、补驱动依赖即可(DataNucleus 支持标准 JDBC):

数据库ConnectionURL 示例额外依赖
MySQLjdbc:mysql://localhost:3306/democom.mysql:mysql-connector-j
PostgreSQLjdbc:postgresql://localhost:5432/demoorg.postgresql:postgresql
H2(文件)jdbc:h2:file:./data/demo内置
Derbyjdbc:derby:./data/demo;create=trueorg.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)(工具篇第六章),生产必须参数化而非字符串拼接。

参考链接