Apache JDO 介绍与入门详解

基于实测介绍 Apache JDO(JSR-243)持久化标准的定位、核心概念与 DataNucleus 字节码增强机制,并给出 jdo-api 3.2.1 + DataNucleus 6.0.x + H2 内存库的最小可运行示例与 JPA/Hibernate/MyBatis 选型对比。

Apache JDOApacheJDOJavaORM2026/10/14

Apache JDO 介绍与入门详解

目标:讲清 Apache JDO(Java Data Objects)是什么、核心概念与字节码增强机制如何运作,并在 Ubuntu 22.04 服务器(OpenJDK 17.0.20.1 + Apache Maven 3.9.16)上从零跑通一个「POJO 增强 + H2 内存库持久化 + JDOQL 查询」的最小工程。全文版本号、坐标、命令与回显均来自真实环境实测,逐行照抄。

一、环境信息

本文所有结论、命令与回显均在以下环境实测取得,读者复现时保持相近版本即可获得一致结果。

  • 操作系统:Ubuntu 22.04(x86_64)服务器
  • JDK:OpenJDK 17.0.20.1(Temurin 发行版,实测版本)
  • 构建工具:Apache Maven 3.9.16
  • 示例工程路径:/work/jdo-demo(Maven 标准目录布局)
  • 组件版本(均为 Maven Central 实测最新 release,核实过程见第四章):
    • javax.jdo:jdo-api 3.2.1
    • org.datanucleus:datanucleus-core 6.0.11
    • org.datanucleus:datanucleus-rdbms 6.0.11
    • org.datanucleus:datanucleus-api-jdo 6.0.5
    • org.datanucleus:datanucleus-maven-plugin 6.0.1
    • com.h2database:h2 2.2.224(示例用内存关系库)

坐标核实与依赖下载都依赖访问 Maven Central,先验证网络可达性:

curl -s -o /dev/null -w "%{http_code}" https://repo1.maven.org/maven2/

输出:

200

💡 说明:200 表示可直连 Maven Central 主仓库 repo1.maven.org。本文所有版本号都以该仓库的 maven-metadata.xml 与目录列表实拍为准,不凭记忆写版本。

二、软件概述与定位

2.1 JDO 是什么

Apache JDO(Java Data Objects)是 Java 平台的持久化标准,由 JCP 以 JSR-243 规范化,标准 API 与 TCK 由 Apache JDO 项目维护(官网 db.apache.org/jdo)。它解决的问题是:让普通 Java 对象(POJO)透明地持久化到数据存储,并在需要时按对象方式查询回来,业务代码面向 javax.jdo.* 标准接口编写,不直接面对 JDBC 的 ResultSet 与 SQL 拼接。

三个关键事实勾出它的轮廓:

  • 标准而非产品:JDO 只定义接口与语义(javax.jdo 包),实际能力由具体实现提供。本文示例代码 import 的全部是 javax.jdo.* 标准类型(见第五章),这既是规范约束,也是实测事实。
  • 透明持久化:开发者写普通 POJO,加少量 JDO 注解标注主键与持久化字段,由「字节码增强器」在编译后改写类的字节码,让类具备持久化能力(详见 3.1)。
  • 实现以 DataNucleus 为主:当前持续维护、通过 JDO TCK 的开源实现是 DataNucleus(其前身是同样出身 Apache 的 JPOX);DataNucleus 同时也是 JPA 的实现之一,长期承担着 JDO 标准的落地角色。

一句话概括:JDO 是 Java 世界里「先于 JPA 出现」的对象持久化标准,用字节码增强实现透明持久化,如今的实际载体是 jdo-api 3.2.1 + DataNucleus 6.0.x。

2.2 版本现状(实测)

JDO 的最新规范与 API 版本是 3.2 线:Maven Central 上 javax.jdo:jdo-api 的最新 release 为 3.2.1(第四章给出 metadata 实拍)。实现侧 DataNucleus 的 6.0.x 线与之配套:core/rdbms 6.0.11、api-jdo 6.0.5、Maven 增强插件 6.0.1,均为实测最新 release。值得注意的是 jdo-api 3.2.1 的包名仍是 javax.jdo(第五章代码 import 可证),尚未迁移到 Jakarta 命名空间。

三、核心概念

本节拆解 JDO 的四个核心机制:PersistenceCapable 与字节码增强、PersistenceManagerFactory/PersistenceManager、JDOQL 查询语言,以及按主键回读。每个机制都给出真实代码片段佐证,完整可运行版本在第五章。

3.1 PersistenceCapable 与字节码增强(enhance)

JDO 规范要求:要被持久化的类必须实现 javax.jdo.PersistenceCapable 契约,这样框架才能在 getter/setter 中织入「字段变更读取/脏状态记录」等持久化逻辑。手写这些样板代码不现实,因此 JDO 采用字节码增强:开发者只写 POJO + 注解,构建期由增强器(Enhancer)自动改写字节码,让类实现持久化契约。

💡 技术拓展:字节码增强的时机不止一种,常见选项对比——

增强时机做法取舍
编译期(Maven/Gradle 插件)在 process-classes 等阶段调用增强器,产物已增强一次配置永久生效;换构建工具需重配
运行期(javaagent)启动时挂 -javaagent 参数,类加载时增强不改构建链;每次启动都要带参数,排障成本高
手工命令行增强手动调用增强器处理 class 文件灵活但易漏,不适合常规流水线

本文采用编译期 Maven 插件方式(datanucleus-maven-plugin 6.0.1),理由:构建产物即增强产物,运行时零额外参数,CI 上可重复。

实体类的写法(本文实测的 demo/Person.java,完整代码见 5.2):@PersistenceCapable(table = "PERSONS") 标注类与表名,@PrimaryKey 标注主键字段,@Persistent 标注持久化字段。增强完成后,DataNucleus 增强器会输出 ENHANCED (Persistable) : demo.Person 这样的确认行(实测回显见 5.4),证明该类已被改写为持久化类。

3.2 PersistenceManagerFactory 与 PersistenceManager

JDO 的运行时入口是一对接口:

  • PersistenceManagerFactory(PMF):重量级工厂,应用生命周期内创建一次,持有连接池与全局配置;
  • PersistenceManager(PM):轻量级会话,从 PMF 获取,承载事务、查询与对象生命周期管理,用完即关。

PMF 通过 JDOHelper.getPersistenceManagerFactory(props) 创建,实现类与连接信息全部由属性表声明。本文实测采用的编程式配置如下(demo/Main.java 摘录):

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("javax.jdo.option.ConnectionDriverName", "org.h2.Driver");
props.setProperty("javax.jdo.option.ConnectionUserName", "sa");
props.setProperty("javax.jdo.option.ConnectionPassword", "");
props.setProperty("datanucleus.schema.autoCreateAll", "true");

PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props);

逐项解读:

  • javax.jdo.PersistenceManagerFactoryClass:指定 PMF 实现类。这里用 DataNucleus 的 JDOPersistenceManagerFactory——标准接口、DataNucleus 实现,正是「规范与实现分离」的直接体现。
  • javax.jdo.option.ConnectionURL:JDBC 连接串。jdbc:h2:mem:testdb 为 H2 内存库,DB_CLOSE_DELAY=-1 让库在 JVM 存续期间保持打开(否则最后一个连接断开内存库即销毁,后续查询会找不到表)。
  • ConnectionDriverName / ConnectionUserName / ConnectionPassword:JDBC 驱动类与账号(H2 默认账号 sa、空密码)。
  • datanucleus.schema.autoCreateAll:DataNucleus 扩展属性,首次运行自动建表——本示例的 PERSONS 表就是这么来的,无需手写 DDL。

💡 说明:JDO 也支持 META-INF/jdoconfig.xml 声明式配置,本文实测选择了编程式 Properties——行为等价,但减少了 XML 配置的失败面,示例更易复现。

3.3 JDOQL 查询语言

JDOQL(JDO Query Language)是 JDO 标准的对象查询语言:以类为「表」、以字段为「列」,支持过滤、排序、参数化与范围查询,语法贴近 Java 表达式。最常用的是单字符串(single-string)形式,本文实测的一条查询:

Query<Person> q = pm.newQuery("SELECT FROM demo.Person WHERE age > 25 ORDER BY age ASC");
List<Person> result = (List<Person>) q.execute();

这段语法的读法:SELECT FROM demo.Person 指定候选类(fully-qualified class name),WHERE age > 25 是对象字段上的过滤条件,ORDER BY age ASC 排序。执行返回 List<Person>,元素就是持久化回来的对象实例(实测结果见 5.5)。

💡 技术拓展:JDO 生态里可用的查询方式不止 JDOQL 单字符串一种——

查询方式形态适用
JDOQL 单字符串一条字符串写完整查询(本文采用)快速、直观,入门首选
JDOQL 类型安全 API以方法链构建查询重构友好,编译期查错
原生 SQL直接执行 SQL复杂报表、数据库特性

本文为最小示例选单字符串形式;工程化项目建议优先类型安全 API。

3.4 按主键回读:getObjectById

除了查询,JDO 还提供按主键直接取对象的标准方法 pm.getObjectById(Person.class, 2L)——第一个参数是实体类,第二个参数是主键值(与 @PrimaryKey 字段对应)。这是「知道主键取单条」的最快路径,实测回读结果见 5.5。

四、版本坐标核实

写入门文档最容易出问题的是版本号过时。本节所有坐标均在 Maven Central(repo1.maven.org)上实拉 maven-metadata.xml 与目录列表核实,回显逐行照抄。

4.1 jdo-api:最新 3.2.1

metadata 实拍(输出节选,实测):

=== javax/jdo/jdo-api ===
    <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>

目录列表实拍(输出节选,实测):

href="../"
href="3.0/"
href="3.0.1/"
href="3.1/"
href="3.1-rc1/"
href="3.2/"
href="3.2.1/"
href="maven-metadata.xml"
...

两份实拍互相印证:metadata 列出 3.0 → 3.2.1 的版本演进,目录里 3.2.1/ 存在,<release> 即 3.2.1。注意 groupId 仍是 javax.jdo,没有 Jakarta 化。

4.2 DataNucleus 四件套:core/rdbms 6.0.11、api-jdo 6.0.5、插件 6.0.1

metadata 实拍(输出节选,实测):

=== org/datanucleus/datanucleus-core ===
    <latest>6.0.11</latest>
    <release>6.0.11</release>
=== org/datanucleus/datanucleus-rdbms ===
    <latest>6.0.11</latest>
    <release>6.0.11</release>
=== org/datanucleus/datanucleus-api-jdo ===
    <latest>6.0.5</latest>
    <release>6.0.5</release>
=== org/datanucleus/datanucleus-maven-plugin ===
    <latest>6.0.1</latest>
    <release>6.0.1</release>

datanucleus-core 版本目录尾部实拍(输出节选,实测):

href="6.0.9/"
href="maven-metadata.xml"
...

汇总成坐标对照表:

组件Maven 坐标最新 release(实测)作用
JDO 标准 APIjavax.jdo:jdo-api3.2.1规范接口与注解(javax.jdo.*)
DataNucleus 内核org.datanucleus:datanucleus-core6.0.11增强器与运行时内核
DataNucleus 关系库适配org.datanucleus:datanucleus-rdbms6.0.11面向关系库(含 H2)的持久化实现
DataNucleus JDO 适配层org.datanucleus:datanucleus-api-jdo6.0.5JDO 标准到内核的桥接
增强器 Maven 插件org.datanucleus:datanucleus-maven-plugin6.0.1构建期执行 enhance
示例关系库com.h2database:h22.2.224内存库,免装数据库服务

对上表的解读:core 与 rdbms 版本号同步(6.0.11),api-jdo 版本线独立(6.0.5),插件 6.0.1——四个版本不要求对齐,各取最新 release 组合即可(本文正是按此组合实测通过的)。目录尾部实拍与 metadata 的 release=6.0.11 相互印证。

五、最小可运行示例(DataNucleus enhance + H2 + JDOQL)

本节从零搭一个最小 Maven 工程:POJO 加 JDO 注解 → 构建期字节码增强 → 写 3 条数据进 H2 内存库 → JDOQL 查询 age > 25 → 按主键回读。每一步都给命令与实测回显。

5.1 工程结构与完整 pom

工程结构:

jdo-demo/
├── pom.xml
└── src
    └── main
        └── java
            └── demo
                ├── Main.java
                └── Person.java

完整 pom.xml(坐标与第四章核实结果一致):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>demo</groupId>
    <artifactId>jdo-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <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>
</project>

pom 要点解读:

  • 依赖五个坐标缺一不可:jdo-api(标准 API)、datanucleus-core(内核)、datanucleus-api-jdo(JDO 适配层)、datanucleus-rdbms(关系库实现)、h2(测试库)。
  • 增强插件绑定在 process-classes 阶段:编译完成后立刻增强,mvn package 天然带上增强步骤。<verbose>true</verbose> 让增强器输出确认行(5.4 可见)。
  • 实测该组合未配置 log4j 依赖进插件 classpath,6.0.1 直接可用,无 NoClassDefFoundError。

5.2 持久化实体类 Person

完整 src/main/java/demo/Person.java:

package demo;

import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(table = "PERSONS")
public class Person {
    @PrimaryKey
    private long id;

    @Persistent
    private String name;

    @Persistent
    private int age;

    public Person(long id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public long getId() { return id; }
    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public String toString() {
        return "Person{id=" + id + ", name='" + name + "', age=" + age + "}";
    }
}

注解逐条解读:

  • @PersistenceCapable(table = "PERSONS"):声明该类可持久化,并指定表名 PERSONS;未加注解的类会被增强器忽略。
  • @PrimaryKey:id 字段为对象主键,对应 getObjectById 的第二参数。
  • @Persistent:声明持久化字段(name、age)。
  • 类本身是普通 POJO——没有任何持久化样板代码,这正是「增强机制」要接手的部分。

5.3 主流程:持久化、JDOQL 查询、按主键回读

完整 src/main/java/demo/Main.java:

package demo;

import java.util.List;
import java.util.Properties;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.jdo.Transaction;

public class Main {
    public static void main(String[] args) {
        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("javax.jdo.option.ConnectionDriverName", "org.h2.Driver");
        props.setProperty("javax.jdo.option.ConnectionUserName", "sa");
        props.setProperty("javax.jdo.option.ConnectionPassword", "");
        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();
        }

        Query<Person> q = pm.newQuery("SELECT FROM demo.Person WHERE age > 25 ORDER BY age ASC");
        List<Person> result = (List<Person>) q.execute();
        System.out.println("[JDOQL] age > 25 -> " + result.size() + " rows");
        for (Person p : result) {
            System.out.println("  " + p);
        }

        Person byId = pm.getObjectById(Person.class, 2L);
        System.out.println("[getObjectById] id=2 -> " + byId);

        pm.close();
        pmf.close();
    }
}

流程解读:第一段在事务里 makePersistent 三条数据并提交;第二段用 JDOQL 单字符串查询 age > 25 并按 age 升序打印;第三段 getObjectById(Person.class, 2L) 按主键回读 id=2 的对象。三段输出相互独立,可分别核对(见 5.5)。

5.4 构建实测:字节码增强

在工程目录执行构建(首次构建会下载全部依赖,耗时较长):

mvn clean package

enhance 阶段真实输出(实测):

[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

验证结果:判定增强成功的两个标志都在回显里——ENHANCED (Persistable) : demo.Person(Person 已被改写为持久化类)与最终 BUILD SUCCESS。Total time: 07:29 min 为首次构建含全部依赖下载的耗时,再次构建只需数秒。

⚠️ 注意:实测中 datanucleus-maven-plugin 6.0.1 会把增强器的 stdout/stderr 以 [ERROR] 级别转发进 Maven 日志,但内容实为 INFO 级别的增强过程信息,不是失败。判定成败只看 ENHANCED (Persistable) 行与 BUILD SUCCESS(详见第八章 Q1)。

5.5 运行实测:H2 持久化与查询回读

通过 exec 插件运行主类:

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

结果逐项验证,三段输出与预期完全吻合:

  • [persist] 3 objects committed:事务提交成功,3 条数据进入 H2 内存库(表 PERSONS 由 autoCreateAll 自动创建)。
  • [JDOQL] age > 25 -> 2 rows:Alice(30)与 Carol(27)满足条件,且按 age 升序输出(27 在前、30 在后)——排序语义正确;Bob(22)被正确过滤。
  • [getObjectById] id=2 -> Person{id=2, name='Bob', age=22}:按主键回读 id=2 命中 Bob,与写入值一致。

至此最小示例闭环完成:POJO → 增强 → 持久化 → 查询 → 主键回读全部按 JDO 标准接口跑通。

💡 说明:实测环境未配置 log4j2 绑定,DataNucleus 的运行期日志(含建表 SQL)默认不输出;持久化是否生效即以上述查询与回读结果为证(详见第八章 Q2)。

六、与 JPA/Hibernate/MyBatis 对比

Java 持久层技术不止一种,选型前必须分清「标准」与「产品」两个层次,再对照自身场景。

💡 技术拓展:Java 主流持久化技术横向对比——

维度JDO(本文)JPAHibernate(原生 API)MyBatis
定位Java 持久化标准(JSR-243)Java 持久化标准(JSR-338)JPA 的主流实现 + 自有 APISQL 映射框架
API 包名javax.jdojavax.persistenceorg.hibernateorg.apache.ibatis
映射方式注解 + 字节码增强注解(@Entity)+ 代理/增强同 JPA 生态XML/注解手写 SQL 映射
透明持久化是(增强实现脏检查)是是否(结果手动映射)
存储范围标准抽象,实现可扩展(DataNucleus 以 RDBMS 为主,另有其他存储插件)面向关系数据库关系数据库关系数据库
生态与社区小众,实现以 DataNucleus 为主Java 持久化事实标准,生态最大JPA 的事实默认实现使用广泛、SQL 可控性强

选型建议:新项目首选 JPA/Hibernate(生态与人才储备最大);需要精确控制 SQL 选 MyBatis;存量 JDO 系统维护或「标准 API + 实现可插」诉求下 JDO 仍是有效选项。注意 JDO 与 JPA 并非互斥——DataNucleus 同时实现两者,同一内核可服务两套标准 API。

七、适用场景与限制

7.1 适用场景

  • 遗留系统维护:早年基于 JDO 构建的系统,继续用 jdo-api 3.2.1 + DataNucleus 6.0.x 升级依赖、修复问题,迁移成本远低于重写成 JPA。
  • 标准抽象优先:业务代码锁定 javax.jdo 标准接口,底层实现可插——换实现不改业务代码是 JDO 的设计初衷。
  • 教学与研究:JDO 的「注解 + 增强 + 标准查询语言」是理解字节码增强与透明持久化机制的良好样本。

7.2 限制与边界

  • 生态小众:新项目社区主流是 JPA/Hibernate,JDO 的第三方资料、组件兼容性都明显弱于 JPA。
  • 构建链多一步增强:任何持久化类都必须经过 enhance,构建排障多一个环节(虽然插件已把它做成了 Maven 默认步骤)。
  • 命名空间未 Jakarta 化:实测坐标 jdo-api 3.2.1 的包名仍是 javax.jdo(见 5.2 代码 import),在全面转向 jakarta.* 的新项目中需注意命名空间兼容性。
  • 运行期日志需自配:DataNucleus 的 SQL 日志等依赖日志绑定,默认配置下不可见(见第八章 Q2),排障时要先补日志配置。

八、常见问题(FAQ)

Q1:构建时 enhance 阶段刷出一堆 [ERROR],是不是失败了?

不是失败,这是 datanucleus-maven-plugin 6.0.1 的已知行为:插件把增强器的 stdout/stderr 以 [ERROR] 级别原样转发进 Maven 日志,而增强器输出的其实是 INFO 级过程信息。判定成败只看两处:

  • 是否出现 ENHANCED (Persistable) : <类名> 行(本文为 demo.Person);
  • 构建最终是否 BUILD SUCCESS。

两者齐备即增强成功,[ERROR] 前缀可忽略。

Q2:运行时看不到 DataNucleus 的建表 SQL 等日志,怎么确认数据真的写进去了?

实测环境未引入 log4j2 绑定,DataNucleus 的运行期日志(含建表 SQL)默认不输出,属正常现象。确认持久化生效不必依赖日志:用 JDOQL 查询与 getObjectById 回读即可证明——本文 5.5 的三段输出(3 条提交、查询命中 2 条、id=2 回读 Bob)就是完整的验证闭环。如需 SQL 日志,为工程补上对应的日志绑定与配置即可。

九、参考链接