Ubuntu 22.04 安装 Apache Torque 指南

在 Ubuntu 22.04 上从零安装 Apache Torque 4.0(4.x 系列最终版),覆盖 JDK 17 与 Maven 环境、Maven 插件生成 OM 类与建表 SQL、官方 jar 手工 classpath 直跑两种方案,并跑通 Derby 内存库上的真实增删改查,命令与回显均实测通过。

Apache TorqueApache Torque2026/10/15

Ubuntu 22.04 安装 Apache Torque 指南

目标:在 Ubuntu 22.04 上从零搭建 Apache Torque 4.0(4.x 系列最终版)的开发与运行环境(JDK 17 + Maven 3.6.3 + Derby 10.14.2.0 嵌入式数据库),跑通「XML schema → OM 类生成 → 建表 SQL 生成 → 建库 → 增删改查」完整链路;本文全部命令与回显均已在 Ubuntu 22.04 实测通过。

Apache Torque 是 Apache DB 项目下的对象关系映射(ORM)框架。与 Hibernate 等基于反射的主流框架不同,Torque 从手写或逆向生成的 XML schema 出发,预生成数据对象(Data Object)、Peer 门面与记录映射器等全部 Java 类,运行时直接调用生成类访问数据库,无反射开销、无运行期配置魔法。它由四个组件构成:torque-runtime(运行时,应用唯一依赖)、torque-generator(代码生成引擎)、torque-maven-plugin(Maven 集成)、torque-templates(OM/SQL 模板包)。本文给出两种落地方式:面向工程开发的 Maven 插件方案,以及无构建体系场景的官方 jar 手工 classpath 方案。

一、环境信息

本节先固定可复现的基线环境,后续所有命令均在此环境执行。列出操作系统、架构、JDK、构建工具与组件版本,便于读者对照复现。

  • 操作系统:Ubuntu 22.04.5 LTS(PRETTY_NAME="Ubuntu 22.04.5 LTS")
  • 架构:x86_64
  • JDK:OpenJDK 17.0.20.1(apt 包 openjdk-17-jdk)
  • 构建工具:Apache Maven 3.6.3(apt 包 maven)
  • 组件版本:Torque 4.0(4.x 系列最终版),运行时坐标 org.apache.torque:torque-runtime:4.0
  • 嵌入式数据库:Apache Derby 10.14.2.0(org.apache.derby:derby,内存模式)
  • Maven 坐标:org.apache.torque:torque-runtime:4.0、org.apache.torque:torque-maven-plugin:4.0、org.apache.torque:torque-templates:4.0
  • 验证用工作目录:/root/torque-demo(Maven 工程)、/root/jar-run(jar 直跑)

系统与版本确认(实时回显):

cat /etc/os-release | head -3
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
uname -m
x86_64

💡 说明:Torque 是库而非服务,环境中不存在需要常驻的守护进程;数据库选用 Derby 嵌入式内存模式(跟随 JVM 存活),因此「安装完成」的判定标准是跑通一个含建表与增删改查的最小程序,而不是某个端口处于监听状态。

二、前置条件

本节说明安装前必须满足的条件。Ubuntu 22.04 自带可用的软件源,联网即可通过 apt 获取 JDK 与 Maven。

  1. 具有 root 或 sudo 权限的 Ubuntu 22.04 系统。
  2. 可访问 Ubuntu 官方软件源(apt update 能正常拉取索引)。
  3. 可访问 Maven 中央仓库 repo1.maven.org(本文实测网络直连可达,Torque 全部构件均托管于 Maven Central,Apache 官方下载站不提供 Torque 的二进制发布)。
  4. 至少 2 GB 可用磁盘空间(JDK + Maven + 依赖合计约 600 MB)。

网络连通性确认(实时回显):

curl -sI --max-time 20 https://repo1.maven.org/maven2/org/apache/torque/torque-runtime/4.0/torque-runtime-4.0.jar | head -3
HTTP/1.1 200 OK
Date: Wed, 15 Oct 2026 01:20:11 GMT
Server: nginx

三、版本选型说明

本节给出 Torque 的版本谱系,帮助读者在 4.x 与后续版本之间做出选择。以下版本列表与发布时间均从 Maven Central 的构件目录实时核验(maven-metadata.xml 与各版本 pom 的 Last-Modified 时间):

版本线最新版发布日期(Maven Central 实测)JDK 基线(官网声明)适用场景
3.x(已淘汰)3.32010 年前后Java 1.3+极老项目兼容,不建议新用
4.x(本文实测)4.02013-02-27JDK 1.6+经典生成式 ORM,单体 runtime,文档最多
5.x5.12022-01-05(5.0 为 2020-08-14)JDK 1.8+模块化改造起点
6.x6.02024-05-08JDK 11+移除老旧依赖后的现代基线
7.x(当前主线)7.02025-07-31JDK 17+当前 production release,最新特性

关于维护状态需要如实说明:Torque 4.0 发布于 2013 年 2 月,此后项目经历了 5.0(2020 年)、5.1(2022 年)、6.0(2024 年)到 7.0(2025 年 7 月 31 日)的持续演进,项目仍在活跃维护,Maven Central 上当前 release 为 7.0;但 4.x 分支自 4.0 之后未再发布补丁版本,4.x 系列的最终版就是 4.0。官网(db.apache.org/torque)当前推荐新项目使用 7.0。

本文实际采用 4.0,理由如下:其一,4.0 是 Torque 生成式 ORM 教程与社区资料覆盖最完整的版本;其二,其运行时为单体 jar(torque-runtime),依赖结构简单(commons-configuration 1.6、commons-dbcp 1.3 等),适合作为学习与老系统对照的基线;其三,实测其对 JDK 17 完全兼容——生成器、编译与运行时全链路跑通(详见 4.7 节),仅有生成代码中 new Integer() 等旧式写法在 JDK 17 编译期产生 15 个 deprecation 警告,功能不受影响。

数据库适配是 4.0 的一个硬约束,实测核验结果:torque-runtime 4.0 内置适配器仅有 Mssql、Hsqldb、Mysql、Derby、Oracle、Postgres、None 七种(jar tf torque-runtime-4.0.jar | grep adapter 实测),不包含 H2 适配器;同时 SQL 生成模板包(org.apache.torque.templates.sql)支持的 DDL 方言也只有 derby、hsqldb、mssql、mysql、oracle、postgresql 六种。因此本文选用 Derby 10.14.2.0:完全嵌入、零安装,且官方模板原生支持。Derby 版本选择上有一个实测踩坑:10.16.1.1 的字节码是 Java 17(class file major 61),在 JDK 11 环境加载驱动直接抛 UnsupportedClassVersionError;10.14.2.0 是 Java 8 字节码,JDK 8/11/17 均实测可运行,故统一采用 10.14.2.0。

版本确认为 4.0 的实测回显:

curl -s --max-time 30 https://repo1.maven.org/maven2/org/apache/torque/torque/maven-metadata.xml | grep -E "latest|<version>" | head -9
    <latest>7.0</latest>
    <release>7.0</release>
    <versions>
      <version>3.3</version>
      <version>4.0-beta1</version>
      <version>4.0</version>
      <version>5.0</version>
      <version>5.1</version>
      <version>6.0</version>
      <version>7.0</version>
    </versions>

四、方案 A:Maven 依赖安装(在线,推荐)

本节面向常规 Java 项目开发,通过 apt 安装 JDK 与 Maven,再用 torque-maven-plugin 从手写 schema 生成 OM 类与建表 SQL,最后引入 torque-runtime 跑通增删改查。这是最贴合工程实践的方式。

4.1 方案说明

方案 A 的链路是:apt 安装 openjdk-17-jdk 与 maven → 创建 Maven 工程,声明 torque-maven-plugin(classpath 模板包模式)与 torque-runtime、Derby 依赖 → 手写 src/main/schema/project-schema.xml → mvn torque:generate 生成 OM 类 → mvn torque:generate -Psql 生成建表 DDL → 编译并运行验证程序。优点是版本由 pom 统一管控、生成与编译一体化;缺点是首次构建需要联网下载依赖。

4.2 安装 JDK 17

Ubuntu 22.04 官方源内置 openjdk-17-jdk,直接安装即可:

apt-get update -qq && apt-get install -y openjdk-17-jdk maven
# 输出节选(实测)
maven is already the newest version (3.6.3-5).
openjdk-17-jdk is already the newest version (17.0.20.1+1-1~22.04).
0 upgraded, 0 newly installed, 0 to remove and 2 not upgraded.

安装后确认版本:

java -version
mvn -v | head -3
openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1-22.04-Ubuntu, mixed mode, sharing)
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64

验证点:java -version 输出 17.0.x、mvn -v 的 Java version 一行同样显示 17.0.20.1,说明 Maven 已绑定 JDK 17。

4.3 创建工程与 schema

建立工程目录与数据库 schema 定义。Torque 的 schema 是一份手写 XML,描述表、列、主键与外键,是整个生成流程的唯一源头:

mkdir -p /root/torque-demo/src/main/schema
mkdir -p /root/torque-demo/src/main/java/com/example/torque
mkdir -p /root/torque-demo/src/main/resources

/root/torque-demo/src/main/schema/project-schema.xml 全文(bookstore 三表示例,直接照抄):

<?xml version="1.0" encoding="UTF-8"?>
<database name="bookstore" defaultIdMethod="native">
  <table name="publisher" description="Publisher table">
    <column name="publisher_id" primaryKey="true" required="true" type="INTEGER" description="Primary Key"/>
    <column name="name" type="VARCHAR" size="128" required="true" description="Publisher Name"/>
  </table>
  <table name="author" description="Author table">
    <column name="author_id" primaryKey="true" required="true" type="INTEGER" description="Primary Key"/>
    <column name="first_name" type="VARCHAR" size="128" required="true" description="First Name"/>
    <column name="last_name" type="VARCHAR" size="128" required="true" description="Last Name"/>
  </table>
  <table name="book" description="Book table">
    <column name="book_id" primaryKey="true" required="true" type="INTEGER" description="Primary Key"/>
    <column name="isbn" required="true" type="VARCHAR" size="24" description="ISBN"/>
    <column name="title" required="true" type="VARCHAR" size="255" description="Book Title"/>
    <column name="author_id" required="true" type="INTEGER" description="Foreign Key Author"/>
    <column name="publisher_id" required="true" type="INTEGER" description="Foreign Key Publisher"/>
    <foreign-key foreignTable="author" name="book_fk_author" onDelete="cascade">
      <reference local="author_id" foreign="author_id"/>
    </foreign-key>
    <foreign-key foreignTable="publisher" name="book_fk_publisher" onDelete="cascade">
      <reference local="publisher_id" foreign="publisher_id"/>
    </foreign-key>
  </table>
</database>

逐项说明关键配置:

  • database name="bookstore":数据库逻辑名,后续 torque.properties 与生成配置都要引用它。
  • defaultIdMethod="native":主键交给数据库自增机制(Derby 下生成 GENERATED BY DEFAULT AS IDENTITY 列),插入时无须手工赋主键。
  • type="INTEGER"、type="VARCHAR"、size="128":Torque 的逻辑类型,由 SQL 模板按目标数据库方言翻译成物理类型。
  • foreign-key + reference:声明 book 到 author、publisher 的外键;onDelete="cascade" 生成级联删除约束。

4.4 编写 pom.xml

/root/torque-demo/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>com.example.torque</groupId>
  <artifactId>torque-demo</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

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

  <dependencies>
    <dependency>
      <groupId>org.apache.torque</groupId>
      <artifactId>torque-runtime</artifactId>
      <version>4.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.derby</groupId>
      <artifactId>derby</artifactId>
      <version>10.14.2.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.derby</groupId>
      <artifactId>derbytools</artifactId>
      <version>10.14.2.0</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.torque</groupId>
        <artifactId>torque-maven-plugin</artifactId>
        <version>4.0</version>
        <dependencies>
          <dependency>
            <groupId>org.apache.torque</groupId>
            <artifactId>torque-templates</artifactId>
            <version>4.0</version>
          </dependency>
        </dependencies>
        <configuration>
          <packaging>classpath</packaging>
          <configPackage>org.apache.torque.templates.om</configPackage>
          <sourceDir>src/main/schema</sourceDir>
          <options>
            <torque.om.package>com.example.torque.om</torque.om.package>
          </options>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <version>3.2.0</version>
        <executions>
          <execution>
            <id>add-torque-sources</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>add-source</goal>
            </goals>
            <configuration>
              <sources>
                <source>target/generated-sources</source>
                <source>src/main/generated-java</source>
              </sources>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

  <profiles>
    <profile>
      <id>sql</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.torque</groupId>
            <artifactId>torque-maven-plugin</artifactId>
            <version>4.0</version>
            <dependencies>
              <dependency>
                <groupId>org.apache.torque</groupId>
                <artifactId>torque-templates</artifactId>
                <version>4.0</version>
              </dependency>
            </dependencies>
            <configuration>
              <packaging>classpath</packaging>
              <configPackage>org.apache.torque.templates.sql</configPackage>
              <sourceDir>src/main/schema</sourceDir>
              <options>
                <torque.database>derby</torque.database>
                <torque.sql.generate.drops>true</torque.sql.generate.drops>
              </options>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
</project>

逐项说明关键配置:

  • torque-runtime:4.0:运行期唯一 Torque 依赖,Maven 会自动解析其传递依赖(commons-configuration 1.6、commons-dbcp 1.3、jcs 1.3、avalon-framework-api 4.3.1 等,共 11 个)。
  • derby:10.14.2.0 + derbytools:10.14.2.0:嵌入式数据库驱动(版本选型理由见第三章)。
  • 插件 packaging=classpath + configPackage=org.apache.torque.templates.om:直接以 classpath 上的模板包为生成配置(OM 生成模板自带 control 文件与全部模板),无须在工程里手写任何 control.xml 或模板;这是 torque-maven-plugin 4.0 最省事的用法。
  • sourceDir=src/main/schema:schema 源文件目录。不设置时插件默认找 src/main/torque-gen/src(实测报 Could not list files in the following Directory ... torque-gen/src),必须显式指向 schema 目录。
  • torque.om.package:生成类的 Java 包名,模板包强制要求该选项,缺失时实测报 Option torque.om.package must be set。
  • build-helper-maven-plugin:torque-maven-plugin 不会把生成目录注册为编译源目录(实测 mvn compile 直接报 No sources to compile),必须用该插件把 target/generated-sources(基类)与 src/main/generated-java(可编辑类)加入编译范围。
  • sql profile:同一个插件换用 org.apache.torque.templates.sql 模板包生成建表 DDL;torque.database=derby 指定方言(该选项同样强制,缺失时报 Option torque.database must be set)。

4.5 生成 OM 类

cd /root/torque-demo && mvn -B torque:generate
[INFO] --- torque-maven-plugin:4.0:generate (default-cli) @ torque-demo ---
[INFO] Generation successful
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] Total time:  1.403 s

查看生成结果(实测清单):

find target/generated-sources src/main/generated-java -name "*.java" | sort
src/main/generated-java/com/example/torque/om/Author.java
src/main/generated-java/com/example/torque/om/AuthorPeer.java
src/main/generated-java/com/example/torque/om/AuthorPeerImpl.java
src/main/generated-java/com/example/torque/om/AuthorRecordMapper.java
src/main/generated-java/com/example/torque/om/Book.java
src/main/generated-java/com/example/torque/om/BookPeer.java
src/main/generated-java/com/example/torque/om/BookPeerImpl.java
src/main/generated-java/com/example/torque/om/BookRecordMapper.java
src/main/generated-java/com/example/torque/om/Publisher.java
src/main/generated-java/com/example/torque/om/PublisherPeer.java
src/main/generated-java/com/example/torque/om/PublisherPeerImpl.java
src/main/generated-java/com/example/torque/om/PublisherRecordMapper.java
target/generated-sources/com/example/torque/om/BaseAuthor.java
target/generated-sources/com/example/torque/om/BaseAuthorPeer.java
target/generated-sources/com/example/torque/om/BaseAuthorPeerImpl.java
target/generated-sources/com/example/torque/om/BaseAuthorRecordMapper.java
target/generated-sources/com/example/torque/om/BaseBook.java
target/generated-sources/com/example/torque/om/BaseBookPeer.java
target/generated-sources/com/example/torque/om/BaseBookPeerImpl.java
target/generated-sources/com/example/torque/om/BaseBookRecordMapper.java
target/generated-sources/com/example/torque/om/BasePublisher.java
target/generated-sources/com/example/torque/om/BasePublisherPeer.java
target/generated-sources/com/example/torque/om/BasePublisherPeerImpl.java
target/generated-sources/com/example/torque/om/BasePublisherRecordMapper.java

Torque 4.0 的生成物按「两层目录」组织,共 24 个 Java 文件:target/generated-sources 下是 12 个 Base* 基类(每次生成重新覆盖,承载全部标准逻辑);src/main/generated-java 下是 12 个无前缀业务类(如 Author、BookPeer,仅首次生成,之后模板以 skip 策略跳过,专门留给用户改写定制)。重新执行 mvn torque:generate 不会覆盖用户对业务类的修改,这是 Torque 生成式 ORM 的核心定制机制。

4.6 生成建表 SQL

同一个插件在 sql profile 下切换为 SQL 模板包,生成 Derby 方言的建表 DDL:

cd /root/torque-demo && mvn -B -q torque:generate -Psql
INFO  org.apache.torque.generator.control.Controller - Processing output torque.sql.ddl
INFO  org.apache.torque.generator.control.Controller - Start generation of File /root/torque-demo/target/generated-sources/project-schema.sql
cp target/generated-sources/project-schema.sql src/main/resources/schema.sql

生成的 /root/torque-demo/src/main/resources/schema.sql 核心内容(实测):

drop table publisher;
drop table author;
drop table book;

CREATE TABLE publisher
(
    publisher_id INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    name VARCHAR(128) NOT NULL,
    PRIMARY KEY(publisher_id));

CREATE TABLE author
(
    author_id INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    first_name VARCHAR(128) NOT NULL,
    last_name VARCHAR(128) NOT NULL,
    PRIMARY KEY(author_id));

CREATE TABLE book
(
    book_id INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    isbn VARCHAR(24) NOT NULL,
    title VARCHAR(255) NOT NULL,
    author_id INTEGER NOT NULL,
    publisher_id INTEGER NOT NULL,
    PRIMARY KEY(book_id));

ALTER TABLE book
    ADD CONSTRAINT book_fk_author
    FOREIGN KEY (author_id)
    REFERENCES author (author_id)
    ON DELETE CASCADE;
ALTER TABLE book
    ADD CONSTRAINT book_fk_publisher
    FOREIGN KEY (publisher_id)
    REFERENCES publisher (publisher_id)
    ON DELETE CASCADE;

验证点:defaultIdMethod="native" 如约生成 GENERATED BY DEFAULT AS IDENTITY 自增列;两条 ADD CONSTRAINT 外键带 ON DELETE CASCADE 级联删除;torque.sql.generate.drops=true 使脚本开头带 drop 语句(对已存在的库可重复执行)。

4.7 编写验证程序并编译运行

验证程序 /root/torque-demo/src/main/java/com/example/torque/Main.java 全文(直接复制):

package com.example.torque;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Statement;
import java.util.List;
import java.util.Properties;

import org.apache.torque.Torque;
import org.apache.torque.criteria.Criteria;

import com.example.torque.om.Author;
import com.example.torque.om.AuthorPeer;
import com.example.torque.om.Book;
import com.example.torque.om.BookPeer;
import com.example.torque.om.Publisher;
import com.example.torque.om.PublisherPeer;

public class Main {

    public static void main(String[] args) throws Exception {
        System.out.println("---- [1] Torque.init ----");
        Properties props = new Properties();
        try (InputStream in = Main.class.getResourceAsStream("/torque.properties")) {
            props.load(in);
        }
        Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
        Torque.init(new org.apache.commons.configuration.MapConfiguration(props));
        System.out.println("Torque.init 完成, 默认数据库=" + Torque.getDefaultDB()
            + ", 数据库注册表键=" + Torque.getDatabases().keySet());

        System.out.println("---- [2] 执行生成的 DDL 建表 ----");
        String raw = readResource("/schema.sql");
        StringBuilder noComment = new StringBuilder();
        for (String line : raw.split("\n")) {
            if (!line.trim().startsWith("--")) {
                noComment.append(line).append("\n");
            }
        }
        String ddl = noComment.toString();
        int done = 0;
        try (Connection conn = Torque.getConnection()) {
            try (Statement mk = conn.createStatement()) {
                mk.execute("CREATE SCHEMA SA");
            } catch (Exception e) {
                System.out.println("CREATE SCHEMA SA: " + e.getMessage().split(":")[0]);
            }
            for (String sql : ddl.split(";")) {
                String s = sql.replace("\r", "").trim();
                boolean keep = s.startsWith("CREATE TABLE") || s.contains("ADD CONSTRAINT");
                if (!keep) {
                    continue;
                }
                try (Statement st = conn.createStatement()) {
                    st.execute(s);
                }
                done++;
            }
        }
        System.out.println("执行 CREATE/ALTER 语句数=" + done + " (3 张表 + 2 个外键)");

        System.out.println("---- [3] 新增 (save / 主键自动生成) ----");
        Publisher pub = new Publisher();
        pub.setName("人民文学出版社");
        pub.save();
        System.out.println("Publisher 落库: name=" + pub.getName() + " 主键=" + pub.getPublisherId());

        Author author = new Author();
        author.setFirstName("贯中");
        author.setLastName("罗");
        author.save();
        System.out.println("Author 落库: " + author.getLastName() + author.getFirstName() + " 主键=" + author.getAuthorId());

        Book book = new Book();
        book.setIsbn("9787020008728");
        book.setTitle("三国演义");
        book.setAuthorId(author.getAuthorId());
        book.setPublisherId(pub.getPublisherId());
        book.save();
        System.out.println("Book 落库: " + book.getTitle() + " 主键=" + book.getBookId()
            + " author_id=" + book.getAuthorId() + " publisher_id=" + book.getPublisherId());

        System.out.println("---- [4] 查询 (Criteria + Peer.doSelect) ----");
        Criteria crit = new Criteria();
        crit.where(AuthorPeer.LAST_NAME, "罗");
        List<Author> authors = AuthorPeer.doSelect(crit);
        for (Author a : authors) {
            System.out.println("doSelect 命中: id=" + a.getAuthorId()
                + " 姓名=" + a.getLastName() + a.getFirstName());
        }

        List<Book> books = BookPeer.doSelectJoinAuthor(new Criteria());
        for (Book b : books) {
            Author a = b.getAuthor();
            System.out.println("doSelectJoinAuthor: 《" + b.getTitle() + "》 作者="
                + a.getLastName() + a.getFirstName());
        }

        System.out.println("---- [5] 更新 (save / retrieveByPK) ----");
        book.setTitle("三国演义(校注本)");
        book.save();
        Book reloaded = BookPeer.retrieveByPK(book.getBookId());
        System.out.println("retrieveByPK(" + book.getBookId() + ") 标题=" + reloaded.getTitle()
            + " ISBN=" + reloaded.getIsbn());

        System.out.println("---- [6] 删除 (doDelete) ----");
        Criteria del = new Criteria();
        del.where(BookPeer.BOOK_ID, book.getBookId());
        BookPeer.doDelete(del);
        System.out.println("删除 book_id=" + book.getBookId()
            + " 后, 剩余 Book 行数=" + BookPeer.doSelect(new Criteria()).size()
            + ", Author 行数=" + AuthorPeer.doSelect(new Criteria()).size()
            + " (外键只级联删 book, 作者保留)");

        Torque.shutdown();
        System.out.println("---- 全部步骤通过 ----");
    }

    static String readResource(String path) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream(path);
             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            byte[] buf = new byte[8192];
            int n;
            while ((n = in.read(buf)) > 0) {
                out.write(buf, 0, n);
            }
            return new String(out.toByteArray(), "UTF-8");
        }
    }
}

程序配套的运行时配置 /root/torque-demo/src/main/resources/torque.properties 全文(直接照抄):

torque.database.default=bookstore
torque.database.bookstore.adapter=derby

torque.dsfactory.bookstore.factory=org.apache.torque.dsfactory.SharedPoolDataSourceFactory
torque.dsfactory.bookstore.connection.url=jdbc:derby:memory:bookstore;create=true
torque.dsfactory.bookstore.connection.user=sa
torque.dsfactory.bookstore.connection.password=q1w2e3r4

逐项说明:torque.database.default 指定默认库;adapter=derby 选择内置 Derby 适配器;dsfactory 键在 4.0 中是 torque.dsfactory.<库名>.factory(注意不是 3.x 的 .factory.class,实测写旧键名报 No DataSourceFactory definition for default DB found);连接三要素 url/user/password 直接交给连接池。代码中 Class.forName("org.apache.derby.jdbc.EmbeddedDriver") 显式注册驱动是实测必需项:Torque 4.0 的连接池基于 commons-dbcp 1.3,其配置注入不认 driver/driverClassName 属性(实测两写法均报 Property ... is not supported by DataSource: DriverAdapterCPDS),而 Derby 10.14.2.0 的 JDBC SPI 声明在部分 JDK 版本(实测 JDK 11)不会被自动加载,显式注册在 JDK 8/11/17 全部可靠。

编译并运行:

cd /root/torque-demo && mvn -B -q compile && echo "EXIT=$?"
mvn -B -q dependency:build-classpath -Dmdep.outputFile=/tmp/cp.txt
java -Dfile.encoding=UTF-8 -cp "target/classes:$(cat /tmp/cp.txt)" com.example.torque.Main
---- [1] Torque.init ----
Torque.init 完成, 默认数据库=bookstore, 数据库注册表键=[bookstore, default]
---- [2] 执行生成的 DDL 建表 ----
执行 CREATE/ALTER 语句数=5 (3 张表 + 2 个外键)
---- [3] 新增 (save / 主键自动生成) ----
Publisher 落库: name=人民文学出版社 主键=1
Author 落库: 罗贯中 主键=1
Book 落库: 三国演义 主键=1 author_id=1 publisher_id=1
---- [4] 查询 (Criteria + Peer.doSelect) ----
doSelect 命中: id=1 姓名=罗贯中
doSelectJoinAuthor: 《三国演义》 作者=罗贯中
---- [5] 更新 (save / retrieveByPK) ----
retrieveByPK(1) 标题=三国演义(校注本) ISBN=9787020008728
---- [6] 删除 (doDelete) ----
删除 book_id=1 后, 剩余 Book 行数=0, Author 行数=1 (外键只级联删 book, 作者保留)
---- 全部步骤通过 ----

结果解读:六组演示全部按预期输出——Torque.init 识别 bookstore 数据源;生成的 DDL 成功建出 3 张表与 2 个外键;三次 save() 主键由 IDENTITY 自动分配(均为 1);Criteria.where 条件查询与 doSelectJoinAuthor 联表查询正确命中;更新后 retrieveByPK 回读标题一致;删除 book 后 Author 保留(级联只作用于 book 的外键)。这表示 Torque 4.0 的生成器与运行时已在 JDK 17 上完整可用。

💡 说明:-Dfile.encoding=UTF-8 用于固定中文输出的字符集。若服务器 LANG 已为 C.UTF-8,可省略;在默认 POSIX locale 下省略会出现中文乱码。

五、方案 B:官方 jar 直用(离线友好)

本节面向没有 Maven/构建体系的场景,从 Maven Central 直接下载 torque-runtime 与其全部依赖 jar,用 javac + java 完成编译运行,不依赖任何构建工具。

5.1 方案说明

链路是:下载 14 个运行期 jar 并校验 sha1 → 复用方案 A 已生成的 24 个 OM 源文件(生成物与构建工具无关,任何方式产出的源码都能编译)→ javac -cp 编译 → java -cp 运行。适用于离线环境、脚本化部署或纯粹想「拿到 jar 就能用」的读者。

5.2 下载依赖 jar 与完整性校验

torque-runtime 4.0 的完整运行期依赖经 mvn dependency:list 实测为 14 个 jar(含 Derby 驱动)。逐一下载(命令较长,分两条列出,全部实测):

mkdir -p /root/jar-run/lib && cd /root/jar-run/lib
R=https://repo1.maven.org/maven2
curl -sfO $R/org/apache/torque/torque-runtime/4.0/torque-runtime-4.0.jar
curl -sfO $R/commons-beanutils/commons-beanutils-core/1.8.0/commons-beanutils-core-1.8.0.jar
curl -sfO $R/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar
curl -sfO $R/commons-configuration/commons-configuration/1.6/commons-configuration-1.6.jar
curl -sfO $R/commons-dbcp/commons-dbcp/1.3/commons-dbcp-1.3.jar
curl -sfO $R/commons-digester/commons-digester/1.8/commons-digester-1.8.jar
curl -sfO $R/commons-lang/commons-lang/2.6/commons-lang-2.6.jar
curl -sfO $R/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar
curl -sfO $R/commons-pool/commons-pool/1.5.6/commons-pool-1.5.6.jar
curl -sfO $R/concurrent/concurrent/1.0/concurrent-1.0.jar
curl -sfO $R/org/apache/avalon/framework/avalon-framework-api/4.3.1/avalon-framework-api-4.3.1.jar
curl -sfO $R/org/apache/jcs/jcs/1.3/jcs-1.3.jar
curl -sfO $R/org/apache/derby/derby/10.14.2.0/derby-10.14.2.0.jar
curl -sfO $R/org/apache/derby/derbytools/10.14.2.0/derbytools-10.14.2.0.jar
ls -1 | wc -l
14

校验两个核心 jar 的 sha1(期望值取自仓库同名 .sha1 文件,与实测值必须逐字一致):

cd /root/jar-run/lib
echo "runtime 期望: $(curl -s $R/org/apache/torque/torque-runtime/4.0/torque-runtime-4.0.jar.sha1)"
echo "runtime 实测: $(sha1sum torque-runtime-4.0.jar | cut -d' ' -f1)"
echo "derby    期望: $(curl -s $R/org/apache/derby/derby/10.14.2.0/derby-10.14.2.0.jar.sha1)"
echo "derby    实测: $(sha1sum derby-10.14.2.0.jar | cut -d' ' -f1)"
runtime 期望: 88a2be8e73374586c480516fc085b2f8fea12968
runtime 实测: 88a2be8e73374586c480516fc085b2f8fea12968
derby    期望: 7efad40ef52fbb1f08142f07a83b42d29e47d8ce
derby    实测: 7efad40ef52fbb1f08142f07a83b42d29e47d8ce

两个核心 jar 的实测大小:torque-runtime-4.0.jar 为 228845 字节,derby-10.14.2.0.jar 为 3232770 字节,14 个 jar 合计约 6.4 MB。

⚠️ 注意:jar 清单必须与 pom 解析结果一致。缺 jar 运行时报 NoClassDefFoundError;多 jar 无害。若不放心清单完整性,可在方案 A 的工程上执行 mvn dependency:list 逐一比对。

5.3 javac 编译与 java 运行

准备源码与资源(OM 生成物 + 验证程序 + 配置):

cd /root/jar-run
mkdir -p src-om src-om-mod res out
cp -r /root/torque-demo/target/generated-sources/com src-om/
cp -r /root/torque-demo/src/main/generated-java/com src-om-mod/
cp /root/torque-demo/src/main/resources/torque.properties /root/torque-demo/src/main/resources/schema.sql res/

编译(24 个 OM 类 + Main,共 25 个编译单元):

javac -encoding UTF-8 -cp "lib/*" -d out \
  src-om/com/example/torque/om/*.java \
  src-om-mod/com/example/torque/om/*.java \
  /root/torque-demo/src/main/java/com/example/torque/Main.java
# 输出节选(实测)
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
15 warnings

验证点:编译成功产出 25 个 class;15 个警告全部是 new Integer(int) 构造器在 JDK 9+ 标记 deprecation 所致(2013 年生成代码的正常现象),不影响运行。

运行(注意 classpath 必须包含 res 目录,否则读不到 torque.properties 实测报 NullPointerException: inStream parameter is null):

java -Dfile.encoding=UTF-8 -cp "out:res:lib/*" com.example.torque.Main
---- [1] Torque.init ----
Torque.init 完成, 默认数据库=bookstore, 数据库注册表键=[bookstore, default]
---- [2] 执行生成的 DDL 建表 ----
执行 CREATE/ALTER 语句数=5 (3 张表 + 2 个外键)
---- [3] 新增 (save / 主键自动生成) ----
Publisher 落库: name=人民文学出版社 主键=1
Author 落库: 罗贯中 主键=1
Book 落库: 三国演义 主键=1 author_id=1 publisher_id=1
---- [4] 查询 (Criteria + Peer.doSelect) ----
doSelect 命中: id=1 姓名=罗贯中
doSelectJoinAuthor: 《三国演义》 作者=罗贯中
---- [5] 更新 (save / retrieveByPK) ----
retrieveByPK(1) 标题=三国演义(校注本) ISBN=9787020008728
---- [6] 删除 (doDelete) ----
删除 book_id=1 后, 剩余 Book 行数=0, Author 行数=1 (外键只级联删 book, 作者保留)
---- 全部步骤通过 ----

验证点:java -cp 一行成功跑完六组结果,且无 NoClassDefFoundError,说明手工 classpath 可用;结果与方案 A 完全一致。

5.4 torque-generator 独立分发包的定位说明

Maven Central 的 org.apache.torque:torque-generator:4.0 另有 bin 分发包(torque-generator-4.0-bin.tar.gz,实测 4822484 字节),解压后为 torque-generator 主 jar 与 8 个依赖 jar。需要说明的是,4.0 版该包内没有命令行启动脚本,主 jar 的 MANIFEST 也没有 Main-Class(实测 jar tf 与 MANIFEST 核验),它面向 Ant 任务或自定义程序内嵌调用。想在无 Maven 环境做 OM 生成,本文 5.3 节的「预生成源码 + javac」方式即为可落地的替代路径;需要纯命令行生成的读者可评估 5.x 及以上版本的 generator 分发包。

六、方案对比与选择建议

本节汇总两种方案的差异,给出选型建议。两种方案运行的是同一份 Torque 4.0 代码与同一批生成类,差别仅在获取与装配方式。

维度方案 A:Maven 插件方案 B:jar 直用
OM 类生成mvn torque:generate 一键生成依赖方案 A 产出的源码(generator 4.0 无 CLI)
运行期依赖Maven 自动解析 14 个 jar手工下载 14 个 jar 并拼 classpath
是否联网首次构建需联网仅下载时需要联网
依赖版本管控pom 统一声明,可锁版本无,靠文件命名区分
完整性校验Maven 自动校验需手工比对 sha1
适用场景常规工程开发离线脚本、临时验证、无构建体系

选型建议:只要工程有构建体系,一律选方案 A——OM 生成是 Torque 的核心工作流,离开 Maven 插件(或 Ant 任务)无法完成;方案 B 的价值在于运行期部署:用方案 A 生成并提交源码后,运行环境完全可以无构建工具。两者也可共存——用方案 A 开发生成,用方案 B 分发运行。

七、配置说明

本节汇总 Torque 4.0 实际需要设置的「配置」,分 Maven 插件配置与运行时配置两组,全部实测核验。

7.1 Maven 插件(生成期)配置

  • packaging=classpath:生成配置的来源模式。classpath 模式从 classpath 上的模板包读取全部配置;默认值 directory 模式则要求工程提供 src/main/torque-gen/conf/control.xml(实测缺文件报 control file file /root/torque-demo/src/main/torque-gen/conf/control.xml not found)。快速上手选 classpath 模式。
  • configPackage=org.apache.torque.templates.om:OM 生成模板包。换 org.apache.torque.templates.sql 即生成 DDL(本文 sql profile)。
  • sourceDir=src/main/schema:schema 文件目录,默认值 src/main/torque-gen/src(实测目录不存在时直接报错)。
  • options.torque.om.package:生成类包名,必填。
  • 生成输出目录:基类固定写 target/generated-sources(${project.build.directory}/generated-sources),可编辑类固定写 src/main/generated-java,两者均由插件默认值决定,一般无需调整。

7.2 torque.properties(运行期)配置

torque.database.default=bookstore
torque.database.bookstore.adapter=derby
torque.dsfactory.bookstore.factory=org.apache.torque.dsfactory.SharedPoolDataSourceFactory
torque.dsfactory.bookstore.connection.url=jdbc:derby:memory:bookstore;create=true
torque.dsfactory.bookstore.connection.user=sa
torque.dsfactory.bookstore.connection.password=q1w2e3r4

逐项说明:

  • torque.database.default:默认数据库逻辑名,Torque.getDefaultDB() 返回它,多库场景下每个库各有一组同名键。
  • adapter:数据库适配器,决定 ID 生成、SQL 方言等行为,可选值实测核验为 mssql/hsqldb/mysql/derby/oracle/postgres/none。
  • dsfactory.<库名>.factory:数据源工厂实现,SharedPoolDataSourceFactory 提供 DBCP 连接池。注意 4.0 的键名到此为止,没有 3.x 时代的 .class 后缀。
  • connection.url/user/password:直通连接池的 JDBC 三要素。connection.* 下其他属性(如 driver)会被 DBCP 拒绝(实测报 Property: driver ... is not supported by DataSource: DriverAdapterCPDS),驱动注册改在代码里显式 Class.forName 完成。
  • torque.dsfactory.bookstore.pool.*:连接池参数(如最大连接数),本文未配置时使用默认值,日常开发够用。

八、多版本管理

Torque 各大版本 artifact 坐标相同、JDK 基线不同,多版本共存靠「隔离依赖坐标 + 隔离 jar 文件名」实现。

Maven 本地仓库中不同版本的构件并列存放,互不覆盖(实时回显):

ls -1 $HOME/.m2/repository/org/apache/torque/torque-runtime/
4.0
5.1
7.0

要点:工程要用哪个版本,只改 pom.xml 的 <dependency><version> 即可,其余版本留在本地仓库供其他工程复用。但注意跨大版本切换不是改数字那么简单:5.x 起运行时拆分为多模块、6.0/7.0 抬高 JDK 基线、部分配置键有变化,切换前应查阅对应版本官网。jar 直跑场景,把不同版本 jar 分目录存放(如 /root/jar-run-7/lib),运行时按需拼 classpath。

切换工程依赖版本并验证生效:

cd /root/torque-demo && mvn -B dependency:tree 2>&1 | grep torque
[INFO] +- org.apache.torque:torque-runtime:jar:4.0:compile
[INFO] |  +- commons-collections:commons-collections:jar:3.2.1:compile
[INFO] |  +- commons-configuration:commons-configuration:jar:1.6:compile
[INFO] |  +- commons-dbcp:commons-dbcp:jar:1.3:compile
[INFO] |  +- commons-lang:commons-lang:jar:2.6:compile
[INFO] |  +- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] |  +- org.apache.jcs:jcs:jar:1.3:compile
[INFO] |  \- org.apache.avalon.framework:avalon-framework-api:jar:4.3.1:compile

验证点:依赖树顶层是 torque-runtime:4.0,其下为核心传递依赖,与手工下载清单一致。

九、常见问题(FAQ)

Q1:mvn torque:generate 报 control file file .../src/main/torque-gen/conf/control.xml not found 怎么办? 插件默认 packaging=directory,要求工程自备 control.xml。快速上手直接在插件配置里写 <packaging>classpath</packaging> 与 <configPackage>org.apache.torque.templates.om</configPackage>,用官方模板包自带的生成配置(见 4.4 节),无需手写任何 control 文件。

Q2:报 Option torque.om.package must be set? OM 模板包要求显式指定生成类包名。在插件 <configuration> 里加 <options><torque.om.package>你的包名</torque.om.package></options>。同理,sql 模板包要求 <torque.database>方言</torque.database>。

Q3:报 Could not list files in the following Directory while reading the sources: .../src/main/torque-gen/src/.? schema 源目录没配对。classpath 模式下插件不会自动用 src/main/schema,必须显式 <sourceDir>src/main/schema</sourceDir>(见 4.4 节)。

Q4:mvn compile 显示 BUILD SUCCESS 但类都编译不到,或运行报 ClassNotFoundException? torque-maven-plugin 不会把生成目录注册为编译源目录。确认 pom 已配置 build-helper-maven-plugin 并把 target/generated-sources 与 src/main/generated-java 加入 <sources>(见 4.4 节);只有 target/generated-sources 被注册时,Base* 类能编过而 Author 等业务类报 cannot find symbol。

Q5:运行报 No suitable driver found for jdbc:derby:...? Torque 4.0 + commons-dbcp 1.3 的组合无法从 torque.properties 注入驱动类名(connection.driver/connection.driverClassName 均被拒绝),Derby 驱动又未必被 JDBC SPI 自动加载(实测 JDK 11 不会、JDK 17 会)。通用解法:在 Torque.init() 之前执行 Class.forName("org.apache.derby.jdbc.EmbeddedDriver")(见 4.7 节)。

Q6:报 No DataSourceFactory definition for default DB found ... under the key torque.dsfactory.bookstore.factory? 4.0 的数据源工厂键名是 torque.dsfactory.<库名>.factory,值直接写工厂类全名;按 3.x 习惯写成 torque.dsfactory.<库名>.factory.class 会被忽略而报此错。

Q7:中文输出显示为乱码(问号)? 默认 POSIX locale 下 JVM 会用 ASCII 输出中文。解决:运行时加 -Dfile.encoding=UTF-8,或把系统 locale 调成 UTF-8(export LANG=C.UTF-8)。

Q8:torque-runtime 4.0 能不能跑在 JDK 8 上? 可以。4.0 官方 JDK 基线为 1.6,本文以 JDK 17 实测全链路通过,JDK 8 向下兼容没有障碍;配套 Derby 用 10.14.2.0(Java 8 字节码)。注意不要把 Derby 升到 10.16.1.1——它是 Java 17 字节码,JDK 11 及以下加载驱动直接抛 UnsupportedClassVersionError(本文第三章实测踩坑记录)。

十、卸载与清理

Torque 本身不产生系统级安装,卸载即移除依赖与本地文件。

移除工程与本地 jar(清理本文全部产物):

rm -rf /root/torque-demo /root/jar-run
ls /root/torque-demo 2>&1
ls: cannot access '/root/torque-demo': No such file or directory

移除 Maven 本地仓库中的 Torque 构件(影响所有工程,慎用):

rm -rf $HOME/.m2/repository/org/apache/torque

卸载 JDK 与 Maven(仅当不再需要)。先以 -s 预演确认将删除的包,无误后去掉 -s 真正执行:

apt-get -s purge -y openjdk-17-jdk maven
# 输出节选(实测)
The following packages will be REMOVED:
  maven* openjdk-17-jdk*
0 upgraded, 0 newly installed, 2 to remove and 2 not upgraded.
Purg maven [3.6.3-5]
Purg openjdk-17-jdk [17.0.20.1+1-1~22.04]

确认无误后真正卸载:

apt-get purge -y openjdk-17-jdk maven && apt-get autoremove -y

十一、参考链接

  1. Apache Torque 官方主页:https://db.apache.org/torque/
  2. Torque 版本与 JDK 基线官方说明(官网首页):https://db.apache.org/torque/
  3. Maven 中央仓库 torque-runtime 坐标页:https://repo1.maven.org/maven2/org/apache/torque/torque-runtime/
  4. Maven 中央仓库 torque-maven-plugin 坐标页:https://repo1.maven.org/maven2/org/apache/torque/torque-maven-plugin/
  5. Maven 中央仓库 torque-generator 坐标页(含 4.0 bin 分发包):https://repo1.maven.org/maven2/org/apache/torque/torque-generator/
  6. Apache Derby 官方站点:https://db.apache.org/derby/
  7. OpenJDK 17 安装包(Ubuntu 官方源):apt-cache show openjdk-17-jdk