Apache JDO 介绍与入门详解
基于实测介绍 Apache JDO(JSR-243)持久化标准的定位、核心概念与 DataNucleus 字节码增强机制,并给出 jdo-api 3.2.1 + DataNucleus 6.0.x + H2 内存库的最小可运行示例与 JPA/Hibernate/MyBatis 选型对比。
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 标准 API | javax.jdo:jdo-api | 3.2.1 | 规范接口与注解(javax.jdo.*) |
| DataNucleus 内核 | org.datanucleus:datanucleus-core | 6.0.11 | 增强器与运行时内核 |
| DataNucleus 关系库适配 | org.datanucleus:datanucleus-rdbms | 6.0.11 | 面向关系库(含 H2)的持久化实现 |
| DataNucleus JDO 适配层 | org.datanucleus:datanucleus-api-jdo | 6.0.5 | JDO 标准到内核的桥接 |
| 增强器 Maven 插件 | org.datanucleus:datanucleus-maven-plugin | 6.0.1 | 构建期执行 enhance |
| 示例关系库 | com.h2database:h2 | 2.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(本文) JPA Hibernate(原生 API) MyBatis 定位 Java 持久化标准(JSR-243) Java 持久化标准(JSR-338) JPA 的主流实现 + 自有 API SQL 映射框架 API 包名 javax.jdo javax.persistence org.hibernate org.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 日志,为工程补上对应的日志绑定与配置即可。
九、参考链接
- Apache JDO 官网:https://db.apache.org/jdo/
- JSR-243(JDO 规范页):https://jcp.org/en/jsr/detail?id=243
- DataNucleus 官网:https://www.datanucleus.org/
- DataNucleus JDO 产品文档:https://www.datanucleus.org/products/datanucleus/jdo/index.html
- Maven Central jdo-api 目录(版本核实):https://repo1.maven.org/maven2/javax/jdo/jdo-api/
- Maven Central DataNucleus 目录:https://repo1.maven.org/maven2/org/datanucleus/