1. 概述

在系列文章的前一篇中,我们展示了如何将 Java 对象持久化到不同的数据存储。更多细节请参考 Java 数据对象指南

JDO 支持多种查询语言,为开发者提供灵活性,使其能使用最熟悉的查询语言。

2. JDO 查询语言

JDO 支持以下查询语言:

  • JDOQL – 使用 Java 语法的查询语言
  • Typed JDOQL – 遵循 JDOQL 语法但提供 API 简化查询使用
  • SQL – 仅用于关系型数据库
  • JPQL – 由 Datanucleus 提供,但非 JDO 规范的一部分

3. 查询 API

3.1 创建查询

创建查询时,需要指定语言和查询字符串:

Query query = pm.newQuery(
  "javax.jdo.query.SQL",
  "select * from product_item where price < 10");

若未指定语言,默认使用 JDOQL:

Query query = pm.newQuery(
  "SELECT FROM com.baeldung.jdo.query.ProductItem WHERE price < 10");

3.2 创建命名查询

可定义查询并通过名称引用。首先创建 ProductItem 类:

@PersistenceCapable
public class ProductItem {

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.INCREMENT)
    int id;
    String name;
    String status;
    String description;
    double price;

    //标准 getter/setter/构造方法
}

然后在 META-INF/package.jdo 中添加类配置定义命名查询:

<jdo>
    <package name="com.baeldung.jdo.query">
        <class name="ProductItem" detachable="true" table="product_item">
            <query name="PriceBelow10" language="javax.jdo.query.SQL">
            <![CDATA[SELECT * FROM PRODUCT_ITEM WHERE PRICE < 10]]>
            </query>
        </class>
    </package>
</jdo>

定义了名为 "PriceBelow10" 的查询。代码中使用方式:

Query<ProductItem> query = pm.newNamedQuery(
  ProductItem.class, "PriceBelow10");
List<ProductItem> items = query.executeList();

3.3 关闭查询

为节省资源,可关闭查询:

query.close();

或通过传递结果集关闭特定结果:

query.close(ResultSet);

3.4 编译查询

调用 compile() 方法验证查询有效性:

query.compile();

若查询无效,将抛出 JDOException

4. JDOQL

JDOQL 是基于对象的查询语言,旨在提供 SQL 的能力,同时保留应用模型中的 Java 对象关系。

JDOQL 查询可定义为单字符串形式。深入前先了解基础概念:

4.1 候选类

候选类必须是可持久化类。使用完整类名而非 SQL 中的表名:

Query query = pm.newQuery("SELECT FROM com.baeldung.jdo.query.ProductItem");
List<ProductItem> r = query.executeList();

示例中 com.baeldung.jdo.query.ProductItem 即为候选类。

4.2 过滤器

过滤器可用 Java 编写,但必须求值为布尔值:

Query query = pm.newQuery("SELECT FROM com.baeldung.jdo.query.ProductItem");
query.setFilter("status == 'SoldOut'");
List<ProductItem> result = query.executeList();

4.3 方法

JDOQL 不支持所有 Java 方法,但支持多种可在查询中调用的方法:

query.setFilter("this.name.startsWith('supported')");

支持方法完整列表见官方文档

4.4 参数

可通过参数传递值。可显式或隐式定义参数。

显式定义参数:

Query query = pm.newQuery(
  "SELECT FROM com.baeldung.jdo.query.ProductItem "
  + "WHERE price < threshold PARAMETERS double threshold");
List<ProductItem> result = (List<ProductItem>) query.execute(10);

或使用 setParameters 方法:

Query query = pm.newQuery(
  "SELECT FROM com.baeldung.jdo.query.ProductItem "
  + "WHERE price < :threshold");
query.setParameters("double threshold");
List<ProductItem> result = (List<ProductItem>) query.execute(10);

隐式定义(不指定参数类型):

Query query = pm.newQuery(
  "SELECT FROM com.baeldung.jdo.query.ProductItem "
  + "WHERE price < :threshold");
List<ProductItem> result = (List<ProductItem>) query.execute(10);

5. 类型化 JDOQL

使用 JDOQLTypedQueryAPI 需准备环境。

5.1 Maven 配置

<dependency>
    <groupId>org.datanucleus</groupId>
    <artifactId>datanucleus-jdo-query</artifactId>
    <version>6.0.1</version>
</dependency>

最新版本见 Maven 中央仓库

5.2 启用注解处理

Eclipse 配置步骤:

  1. 进入 Java Compiler,确保编译器级别 ≥ 1.8
  2. 进入 Java Compiler → Annotation Processing,启用项目特定设置并激活注解处理
  3. 进入 Java Compiler → Annotation Processing → Factory Path,启用项目特定设置,添加以下 JAR:javax.jdo.jardatanucleus-jdo-query.jar

配置后,编译可持久化类时,datanucleus-jdo-query.jar 中的注解处理器会为每个 @PersistenceCapable 注解类生成查询类。示例中生成 QProductItem 类(类名前缀 Q)。

5.3 创建类型化 JDOQL 查询

JDOQLTypedQuery<ProductItem> tq = pm.newJDOQLTypedQuery(ProductItem.class);
QProductItem cand = QProductItem.candidate();
tq = tq.filter(cand.price.lt(10).and(cand.name.startsWith("pro")));
List<ProductItem> results = tq.executeList();

利用生成的查询类访问候选字段并调用其 Java 方法。

6. SQL

使用关系型数据库时,JDO 支持 SQL 查询:

Query query = pm.newQuery("javax.jdo.query.SQL","select * from "
  + "product_item where price < ? and status = ?");
query.setClass(ProductItem.class);
query.setParameters(10,"InStock");
List<ProductItem> results = query.executeList();

通过 setClass() 指定返回 ProductItem 对象,否则返回 Object 类型。

7. JPQL

JDO DataNucleus 提供 JPQL 支持:

Query query = pm.newQuery("JPQL","select i from "
  + "com.baeldung.jdo.query.ProductItem i where i.price < 10"
  + " and i.status = 'InStock'");
List<ProductItem> results = (List<ProductItem>) query.execute();

实体名为完整类名(因 JDO 无 JPA 式实体名元数据)。定义别名 i 后可用其引用 ProductItem。JPQL 语法细节见官方文档

8. 总结

本文展示了 JDO 支持的多种查询语言,包括: ✅ 命名查询的复用方式 ✅ JDOQL 核心概念(候选类/过滤器/方法/参数) ✅ 类型化 JDOQL 的配置与使用 ✅ SQL 和 JPQL 在 JDO 中的集成

合理选择查询语言可显著提升开发效率,避免踩坑。