Apache Torque 4.0 生成器与运行时实战详解
以 bookstore 示例为主线深入拆解 Apache Torque 4.0 的生成器与运行时:schema XML 逐元素讲解、torque-maven-plugin 参数与两级输出机制、生成物解剖、Derby 上的完整增删改查与 API 锚点,全部代码与回显实测通过。
Apache Torque 4.0 生成器与运行时实战详解
目标:以 bookstore(出版社/作者/图书)示例为主线,深入拆解 Apache Torque 4.0(4.x 系列最终版,2013-02-27 发布)的两大核心——代码生成器与运行时:schema XML 逐元素讲解、生成器参数与两级输出机制、生成物逐类解剖、运行时初始化与增删改查逐 API 分析,并给出与 3.x 资料的 API 锚点差异表;本文全部代码与回显已在 Ubuntu 22.04(OpenJDK 17.0.20.1 + Maven 3.6.3 + Derby 10.14.2.0)实测通过。
Torque 的世界观可以概括为一句话:把数据库结构写成 XML,让生成器把 Java 代码长出来,运行时直接调用这些代码。它不基于反射访问用户类——这是官网对 Torque 的第一句定位(原文:unlike most other object-relational mappers, Torque does not use reflection to access user-provided classes)。理解这一点后,Torque 的使用就分解为三件事:写 schema、跑生成器、调生成类。本文按此主线展开。
一、环境信息
本节固定实测环境基线,所有代码片段与回显均来自该环境,便于读者逐条复现。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- JDK:OpenJDK 17.0.20.1(
java -version实测:openjdk version "17.0.20.1" 2026-08-18) - 构建工具:Apache Maven 3.6.3
- 组件版本:Torque 4.0(
org.apache.torque:torque-runtime:4.0、torque-maven-plugin:4.0、torque-templates:4.0、torque-generator:4.0) - 嵌入式数据库:Apache Derby 10.14.2.0(内存模式,
jdbc:derby:memory:bookstore) - 工程目录:
/root/torque-demo(工程与示例同《Ubuntu 22.04 安装 Apache Torque 指南》,pom、schema、torque.properties、Main.java 全文见该文 4.4/4.3/4.7 节,本文直接引用其生成物与运行结果做深度分析)
版本补充说明:Torque 的版本谱系为 3.3 → 4.0(2013-02-27)→ 5.0(2020-08-14)→ 5.1(2022-01-05)→ 6.0(2024-05-08)→ 7.0(2025-07-31,当前 release,要求 JDK 17+)。项目仍在活跃维护,但 4.x 分支停在 4.0,本文即以该最终版为对象。
💡 说明:选用 Derby 而非 MySQL/H2 的原因——torque-runtime 4.0 内置适配器实测仅有 mssql/hsqldb/mysql/derby/oracle/postgres/none 七种(
jar tf torque-runtime-4.0.jar | grep adapter),无 H2;SQL 模板包的 DDL 方言同样没有 h2。Derby 完全嵌入、零安装、被官方模板原生支持,是 4.0 实战的最短路径。
二、实测结论总览
在逐层拆解之前,先给出端到端实测结论,作为后文所有细节的「地图」。六步链路在实测中全部通过(完整回显见《Ubuntu 22.04 安装 Apache Torque 指南》4.7 节):
- 生成:
mvn torque:generate以 classpath 模板包模式生成 24 个 Java 文件,输出Generation successful。 - 生成 SQL:
mvn torque:generate -Psql生成 Derby 方言 DDL(3 个CREATE TABLE+ 2 个ADD CONSTRAINT,主键为GENERATED BY DEFAULT AS IDENTITY)。 - 建库:运行时执行该 DDL,实测「执行 CREATE/ALTER 语句数=5」。
- 增:3 个
save(),主键由 IDENTITY 自动分配(三个主键实测均为 1)。 - 查改:
Criteria.where条件查询命中、doSelectJoinAuthor联表正确、retrieveByPK回读更新后标题一致。 - 删:
doDelete删除 book 后 Author 保留(级联删除只作用于 book 的外键),实测「剩余 Book 行数=0, Author 行数=1」。
一句话总结:Torque 4.0 在现代 JDK(17)上生成器与运行时均完整可用,代价是三点必须按 4.0 的真实 API 使用——Torque.init 收 commons-configuration 的 Configuration、条件查询用 Criteria.where(没有 add)、数据源工厂键名为 torque.dsfactory.<库名>.factory(细节见第七、八节)。
三、组件构成与生成式 ORM 思路
Torque 4/5 由四个 Maven 构件组成,分工清晰:
| 构件 | 坐标(版本 4.0) | 角色 |
|---|---|---|
| torque-runtime | org.apache.torque:torque-runtime | 运行时:连接池装配、Criteria 翻译、事务与 OM 基类逻辑,应用唯一依赖 |
| torque-generator | org.apache.torque:torque-generator | 生成引擎:读取 control 配置、遍历 XML 源、驱动模板产出代码 |
| torque-maven-plugin | org.apache.torque:torque-maven-plugin | 把生成引擎接入 Maven 生命周期的插件 |
| torque-templates | org.apache.torque:torque-templates | 模板包:OM 类模板与 SQL DDL 模板的成品配置(velocity 模板 + outlet 定义) |
💡 技术拓展:ORM 生成式 vs 运行时映射。ORM 处理「表结构与 Java 对象的对应」有两条路线:一是运行时反射映射(Hibernate/JPA 代表——实体是用户手写的普通类,框架启动时用反射读注解/配置建立映射,改表结构改注解即可,灵活但启动有解析成本、字段访问走反射);二是预生成代码(Torque 代表——映射关系在生成期固化进基类,运行时零反射零解析,且基类与业务子类分层,用户可直接改子类定制)。Torque 官方还强调了第三点差异:schema XML 既是生成 Java 类的源头,也能直接生成建表 SQL,做到「一份 schema 定义,代码与库表同源」。本文实测所用路线为 Torque 的生成式:schema → torque-templates 模板包 → 24 个类(12 基类 + 12 业务类),运行时通过 Peer 静态方法访问数据库。
生成式路线的直观代价是「改表必须重跑生成器」,但它换来两个实测可验证的收益:其一,BaseBookPeer 里的联表方法 doSelectJoinAuthor/doSelectJoinPublisher 是编译期就存在的普通静态方法(实测 grep doSelectJoin BaseBookPeer.java 可见),IDE 全程可导航可补全;其二,运行日志中没有任何框架级的反射装配过程,Torque.init 之后即可直接调用。
四、schema 定义详解
schema XML 是整个 Torque 工作的唯一源头,本节按元素层级拆解实测所用的 project-schema.xml(全文见安装指南 4.4 节)。
4.1 database 与 table
<database name="bookstore" defaultIdMethod="native">
name:数据库逻辑名。它出现在三处:运行时torque.properties的torque.database.bookstore.adapter=derby、生成类里的常量BaseAuthorPeer.DATABASE_NAME(实测值"bookstore")、运行时Torque.getDatabases().keySet()(实测输出[bookstore, default])。defaultIdMethod="native":库级默认主键策略,可被表级idMethod覆盖。可选值native(交给数据库自增)、idbroker(Torque 自带的 ID 表机制)、none(主键由应用赋值)。
<table> 定义一张表,name 生成 Java 类名(author → Author/AuthorPeer),description 生成 Javadoc 注释。schema 文件按约定放 src/main/schema/ 且以 schema.xml 结尾(SQL 模板包实测匹配 *schema.xml)。
4.2 column 与类型
<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"/>
type是 Torque 逻辑类型(INTEGER/VARCHAR/DATE/TIMESTAMP 等),由 SQL 模板按方言翻译:实测INTEGER+ Derby →INTEGER,VARCHAR size=128→VARCHAR(128)。primaryKey="true":主键成员。native策略下生成GENERATED BY DEFAULT AS IDENTITY(实测 DDL),对应 Java 侧setAuthorId(int)与 IDENTITY 自动取号。required="true":NOT NULL。size:VARCHAR/CHAR 长度。description:进 Javadoc(实测生成类头部含The skeleton for this class was autogenerated by Torque)。
生成后的 Java 属性名遵循驼峰规则:author_id → authorId,getter/setter 为 getAuthorId()/setAuthorId(int)(实测 BaseAuthor 方法清单可见)。
4.3 foreign-key 与级联
<foreign-key foreignTable="author" name="book_fk_author" onDelete="cascade">
<reference local="author_id" foreign="author_id"/>
</foreign-key>
foreignTable+reference对:声明 book.author_id → author.author_id。它同时产出两层代码:数据库层的外键约束(实测 DDLALTER TABLE book ADD CONSTRAINT book_fk_author ... ON DELETE CASCADE)与 Java 层的联表方法(实测BaseBookPeer.doSelectJoinAuthor(Criteria),以及BaseBook.setAuthor(Author)关系 setter)。onDelete="cascade":级联删除。实测行为:删除 book 不影响 author(级联只沿外键方向生效,book 是子表)。name:约束名,实测原样进入 DDL(book_fk_author)。
4.4 主键策略的实测验证
defaultIdMethod="native" 在 Derby 下的完整效果(实测 DDL 与运行回显):三张表的主键列均为 INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY;插入代码从不赋主键,save() 后从对象上读回数据库分配的值——实测三个对象的主键输出均为 1(新内存库首行)。这正是生成式 ORM「库表同源」的体现:ID 策略在 schema 声明,DDL 与 Java API 从同一处派生,无需两处对齐。
五、生成器详解
torque-generator 4.0 是一个通用生成引擎(可生成任何东西),OM 类生成由 torque-templates 模板包驱动。本节拆解插件用法、两级输出机制与生成物。
5.1 torque-maven-plugin 的 goal 与参数
实测 mvn org.apache.torque:torque-maven-plugin:4.0:help:4.0 只有一个 goal——generate(SQL 生成也是它,换模板包而已)。核心参数实测清单(参数名与语义来自插件 sources 核验,行为来自实测):
| 参数 | 默认值 | 说明 |
|---|---|---|
| packaging | directory | 生成配置来源:directory(工程内 torque-gen 目录)/ jar(配置 jar)/ classpath(classpath 上的配置包) |
| configPackage | 无 | classpath 模式必填,如 org.apache.torque.templates.om(OM 类模板包) |
| configDir | src/main/torque-gen | directory 模式的配置根目录(control.xml 在其 conf/ 子目录) |
| sourceDir | src/main/torque-gen/src | schema 源文件目录(classpath 模式实测必须显式改为 src/main/schema) |
| sourceIncludes | *schema.xml | 源文件匹配模式 |
| defaultOutputDir | ${project.build.directory}/generated-sources | 默认输出目录(基类落点) |
| outputDirMap | modifiable → src/main/generated-java | 命名输出目录映射(可编辑类落点) |
| options / optionsFile | 无 | 注入模板变量,如 torque.om.package、torque.database |
| overrideConfigDir / overrideConfigPackage | 无 | 覆盖部分配置(进阶定制用,本文未启用) |
| workDir | ${project.build.directory}/torque-gen | 生成器工作目录 |
最有价值的组合就是安装指南 4.4 节的最小配置:packaging=classpath + configPackage=org.apache.torque.templates.om + sourceDir=src/main/schema + options.torque.om.package。classpath 模式直接消费 torque-templates jar 内置的完整生成配置(control + 全部 velocity 模板 + outlet 定义,实测模板包内含 org/apache/torque/templates/om/conf/control.xml 与 templates/ 目录树),工程零模板文件。
5.2 两级输出机制:base 覆盖、业务 skip
Torque 4.0 生成器的输出分两级,这是它区别于多数生成器的设计:
target/generated-sources(base 层):12 个Base*类(BaseAuthor/BaseAuthorPeer/BaseAuthorPeerImpl/BaseAuthorRecordMapper × 3 表)。每次执行mvn torque:generate重新生成并覆盖,承载全部标准逻辑。src/main/generated-java(modifiable 层):12 个业务类(Author/AuthorPeer/AuthorPeerImpl/AuthorRecordMapper × 3 表)。仅首次生成,输出策略为 skip(目标文件已存在则跳过),专门留给用户改写。实测业务类头部注释原文:You should add additional methods to this class to meet the application requirements. This class will only be generated as long as it does not already exist in the output directory.
由此形成的定制闭环是:改 schema → 重跑生成器 → base 层更新(新字段、新 join 方法自动出现)→ 手写在业务类里的定制代码原样保留。实测重跑 mvn torque:generate 多次均正常结束(Generation successful),业务类不被触碰。
💡 说明:两层目录都必须注册进编译范围,torque-maven-plugin 不会自动注册(实测
mvn compile报No sources to compile),安装指南用 build-helper-maven-plugin 的<source>target/generated-sources</source><source>src/main/generated-java</source>完成注册。
5.3 生成物解剖
以 author 表为例看三类生成物各自的角色(实测方法与常量清单):
业务类 Author.java(modifiable 层)——空壳定制点:
public class Author
extends com.example.torque.om.BaseAuthor
{
/** Serial version */
private static final long serialVersionUID = 1792069794612L;
}
数据对象 BaseAuthor.java(base 层)——字段访问与持久化入口(实测方法签名节选):
public void setAuthorId(int v)
public void setFirstName(String v)
public void setLastName(String v)
public void save() throws TorqueException
public void save(String dbName)
public void save(Connection con)
public void setPrimaryKey(ObjectKey key)
save() 有三个重载:无参(用默认库)、带库名(多库场景)、带 Connection(手工事务)。对象自带 setNew/setModified/setSaving/setLoading/setDeleted 五个状态开关(实测方法清单),save 依据 new/modified 状态决定 insert 还是 update。
门面 BaseAuthorPeer.java(base 层)——静态查询入口(实测常量与方法节选):
public static final String DATABASE_NAME;
public static final String TABLE_NAME;
public static final TableMap TABLE;
public static final ColumnMap AUTHOR_ID;
public static final ColumnMap FIRST_NAME;
public static final ColumnMap LAST_NAME;
public static final int numColumns = 3;
public static List<Author> doSelect(Criteria criteria)
public static List<Author> retrieveByPKs(Collection<ObjectKey> pks)
AUTHOR_ID 等常量是 ColumnMap 类型,作为 Criteria.where() 的左值(实测第六节查询代码即用 AuthorPeer.LAST_NAME);Book 侧的 BaseBookPeer 额外生成 doSelectJoinAuthor(Criteria) 与 doSelectJoinPublisher(Criteria)(实测 grep 确认),一次联表直接取回带关系对象的列表。
三类之外还有 PeerImpl(Peer 的可替换实现,经 Manager/工厂定位,测例未定制)与 RecordMapper(把 JDBC ResultSet 行转成对象,无反射——替代老版本 doSelect 内部逻辑的关键件)。24 个文件 = 3 表 × (1 数据对象 + 1 Peer + 1 PeerImpl + 1 Mapper) × 2 层。
5.4 SQL 生成
把插件 configPackage 换成 org.apache.torque.templates.sql(安装指南的 sql profile),同一份 schema 即产出建表 DDL(实测 target/generated-sources/project-schema.sql):
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;
选项 torque.database=derby 决定方言(可选 derby/hsqldb/mssql/mysql/oracle/postgresql,实测模板包仅此六种);torque.sql.generate.drops=true 使脚本含 drop 段。SQL 与 OM 类出自同一份 schema,天然一致——这正是官网描述的「The XML schema can also be used to generate and execute a SQL script which creates all the tables in the database」。
六、运行时详解
运行时(torque-runtime 4.0)的全部工作围绕三个对象展开:Torque(初始化与连接管理入口)、Criteria(查询描述)、生成类(Peer 静态方法 + 数据对象)。本节按实测六步逐 API 拆解。
6.1 torque.properties 与 Torque.init
运行时配置实测全文(安装指南 4.7 节,共 5 行有效配置):
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
初始化代码与实测回显:
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));
Torque.init 完成, 默认数据库=bookstore, 数据库注册表键=[bookstore, default]
三个关键点(均为 4.0 实测锚点):
Torque.init的参数类型:4.0 接受 commons-configuration 1.6 的org.apache.commons.configuration.Configuration接口(MapConfiguration/PropertiesConfiguration均可),而非Properties或 3.x 风格的文件路径重载——网上流传的Torque.init("torque.properties")写法在 4.0 编译不过(实测)。- 数据源工厂键名:
torque.dsfactory.<库名>.factory,值直接写工厂类全名;3.x 的torque.dsfactory.<库名>.factory.class键在 4.0 被忽略(实测报No DataSourceFactory definition for default DB found)。 - 驱动注册:
Class.forName必须保留。Torque 4.0 的连接池是 commons-dbcp 1.3 的DriverAdapterCPDS,Torque 无法从配置注入驱动类(实测connection.driver与connection.driverClassName都报Property ... is not supported by DataSource: DriverAdapterCPDS),而 Derby 10.14.2.0 的 JDBC SPI 声明在 JDK 11 不会被自动加载(JDK 17 会),显式注册在 8/11/17 全部可靠。
6.2 连接获取
try (Connection conn = Torque.getConnection()) {
// 原生 JDBC 或建表 DDL
}
Torque.getConnection() 无参取默认库连接,出池入池由 SharedPoolDataSourceFactory 管理,支持 try-with-resources(底层是池化代理连接,close 即归还)。实测用它执行了 5 条 DDL(3 个 CREATE TABLE + 2 个 ALTER TABLE ADD CONSTRAINT)。它是逃生通道:生成类覆盖不到的 SQL(建表、批量维护)走原生 JDBC,与 OM 体系共享同一个池。
6.3 新增:save 与主键回填
Author author = new Author();
author.setFirstName("贯中");
author.setLastName("罗");
author.save();
Author 落库: 罗贯中 主键=1
new Author() 后对象处于 new 状态,save() 走 insert 路径;主键列(native 策略)不由代码赋值,插入后 Torque 通过 JDBC 取回 IDENTITY 生成的值并回填对象(实测 getAuthorId() 返回 1)。book 插入时只需 setAuthorId(author.getAuthorId())(或生成的关系 setter setAuthor(Author)),实测输出 author_id=1 publisher_id=1 与库中数据一致。
6.4 查询:Criteria.where 与 Peer.doSelect
Torque 4.0 重写了 Criteria 体系(新类在 org.apache.torque.criteria 包),条件用 where:
Criteria crit = new Criteria();
crit.where(AuthorPeer.LAST_NAME, "罗");
List<Author> authors = AuthorPeer.doSelect(crit);
doSelect 命中: id=1 姓名=罗贯中
实测锚点:4.0 的新 Criteria 没有 add(Column, Object) 方法(老资料最常见的写法),列对象是 AuthorPeer.LAST_NAME(ColumnMap 类型)直接作 where 左值;等值、区间、like 由 where 重载与 Criteria 的 AND/OR 组合表达。doSelect 返回 List<Author>(泛型直出,内部走生成的 AuthorRecordMapper,无反射逐字段装配)。
联表查询直接用生成方法:
List<Book> books = BookPeer.doSelectJoinAuthor(new Criteria());
doSelectJoinAuthor: 《三国演义》 作者=罗贯中
一次调用取回 Book 且其 getAuthor() 返回完整 Author 对象(实测直接打印关系字段成功)——join 方法由 schema 的 foreign-key 生成,覆盖 book 对两个外键的全部组合(实测 BaseBookPeer 有 doSelectJoinAuthor 与 doSelectJoinPublisher)。
6.5 更新:save 复用与 retrieveByPK
book.setTitle("三国演义(校注本)");
book.save();
Book reloaded = BookPeer.retrieveByPK(book.getBookId());
retrieveByPK(1) 标题=三国演义(校注本) ISBN=9787020008728
同一个 save() 承担 update:从库里 doSelect 出来的对象处于非 new 状态,改字段后 save 走 update 路径。retrieveByPK 按主键取单行(主键类型由 schema 派生,int 直接可传,实测回读标题一致,证明 update 落库)。
6.6 删除:doDelete 与级联验证
Criteria del = new Criteria();
del.where(BookPeer.BOOK_ID, book.getBookId());
BookPeer.doDelete(del);
删除 book_id=1 后, 剩余 Book 行数=0, Author 行数=1 (外键只级联删 book, 作者保留)
doDelete(Criteria) 按条件删除;实测同时验证了 DDL 里 ON DELETE CASCADE 的方向语义——删除 book(子表)不影响 author(父表),author 行保留。Peer 还有 doDelete(ObjectKey) 单键重载。
6.7 退出与资源清理
Torque.shutdown();
shutdown() 关闭连接池并(对 Derby 嵌入式库)释放库文件句柄;对内存库(jdbc:derby:memory:bookstore)而言,库随 JVM 退出而消失,重启即全新库(配合 create=true 自动重建),实测多次重复运行程序结果完全一致。
七、API 锚点与版本差异
4.0 的公开资料以 2013 年为主,而网络流传代码大量混入 3.x 写法。本表汇总实测核验过的差异锚点,按「老写法 → 4.0 实测写法」组织:
| 主题 | 3.x / 流传写法 | 4.0 实测写法 |
|---|---|---|
| 初始化 | Torque.init("torque.properties")(字符串路径重载) | Torque.init(Configuration),配置用 commons-configuration 1.6 的实现类包装(如 MapConfiguration) |
| 条件 | criteria.add(AuthorPeer.LAST_NAME, "罗")(org.apache.torque.util.Criteria) | criteria.where(AuthorPeer.LAST_NAME, "罗")(org.apache.torque.criteria.Criteria,4.0 新包新类) |
| 数据源工厂 | torque.dsfactory.<库>.factory.class=... | torque.dsfactory.<库>.factory=...(无 .class 后缀) |
| 连接池属性 | connection.driver=驱动类名 | 不支持(实测报 not supported),驱动用代码 Class.forName 注册 |
| 生成器入口 | torque:* 多 goal(3.3 时代 maven1 插件) | 仅 torque:generate 一个 goal,OM/SQL 靠换模板包 |
| 生成配置 | 全套工程内 control/模板 | packaging=classpath + configPackage=org.apache.torque.templates.om 即可 |
| 生成物布局 | 单目录(3.x) | 两级:base 到 target/generated-sources(覆盖),业务类到 src/main/generated-java(skip 保留定制) |
| 查询结果装配 | Peer 内部逐字段手工映射 | 生成的 *RecordMapper 专职映射,doSelect 返回强类型 List |
对照 JDK 兼容性实测结论:4.0 官方基线 JDK 1.6,实测 JDK 17 全链路可用;唯一摩擦是生成代码里 new Integer(int) 等 2013 年写法带来 15 个 deprecation 警告(功能无损)。若新项目无历史包袱,官网当前推荐 7.0(JDK 17+ 基线、现代依赖树),4.0 的价值在于存量系统与经典生成式范式的对照研究。
八、踩坑实录
以下问题均在本次实测中真实遇到并解决,按出现顺序记录,供排障时对号入座。
control file ... not found:首次执行torque:generate,工程未写 control.xml。原因:插件默认packaging=directory。解决:改packaging=classpath+configPackage=org.apache.torque.templates.om。Could not list files in the following Directory .../src/main/torque-gen/src/.:classpath 模式下源目录默认值不是src/main/schema。解决:显式<sourceDir>src/main/schema</sourceDir>。Option torque.om.package must be set:OM 模板包要求生成包名。解决:插件<options>注入。SQL 模板包同理要求torque.database(实测缺失时报同名格式错误)。No sources to compile:生成成功但编译不到类。原因:插件不注册生成目录为源目录。解决:build-helper-maven-plugin 添加两级目录。No DataSourceFactory definition for default DB found ... under the key torque.dsfactory.bookstore.factory:按 3.x 键名写factory.class。解决:去掉.class后缀。Property: driver ... is not supported by DataSource: DriverAdapterCPDS(SEVERE 日志):配置里给connection.driver/connection.driverClassName都无效。解决:删掉该配置项,代码里Class.forName显式注册。No suitable driver found for jdbc:derby:...:JDK 11 下 Derby 10.14.2.0 的 SPI 不自动生效。解决:同上,Class.forName(置于Torque.init之前)。UnsupportedClassVersionError ... class file version 61.0:Derby 10.16.1.1 是 Java 17 字节码,JDK 11 加载驱动即炸。解决:Derby 固定 10.14.2.0(Java 8 字节码,8/11/17 全兼容)。- 中文输出变
?????:POSIX locale 下 JVM 默认 ASCII 编码。解决:-Dfile.encoding=UTF-8或LANG=C.UTF-8。 - 运行报
NullPointerException: inStream parameter is null:jar 直跑时 torque.properties 不在 classpath。解决:把含配置的目录加进-cp。
九、常见问题(FAQ)
Q1:生成器能从已有数据库逆向生成 schema 吗? 官网明确该能力存在(XML schema「can either be written by hand or a starting point can be generated from an existing database」),对应模板包为逆向工程配置。本文实测范围限于手写 schema 的正向链路,逆向流程未实测,使用前请按官网 generator 文档验证目标数据库的适配情况。
Q2:一套 schema 能同时生成 OM 类和 SQL 吗?
能,但 4.0 的单次 torque:generate 只消费一个模板包(一个 configPackage),OM 与 SQL 需要执行两次(安装指南的做法:默认配置生成 OM,sql profile 生成 DDL)。两次都从同一份 schema 读取,产出天然一致。
Q3:业务类(Author/BookPeer)被手改后重新生成会丢吗? 不会。modifiable 层输出策略为 skip,目标文件存在即跳过(生成类头注释原文与 5.2 节实测)。配套建议:业务类里只写定制逻辑,别动 base 类——base 每次重生成,改动必丢。
Q4:torque-runtime 4.0 依赖体积有多大? 运行期共 14 个 jar 约 6.4 MB(实测,清单见安装指南 5.2 节),其中 derby 一个 jar 占 3.1 MB;Torque 自身运行时 jar 仅 228845 字节(实测文件大小),连接池(commons-dbcp/commons-pool)、配置(commons-configuration/commons-lang)、缓存(jcs)各自独立,可按需裁剪。
Q5:除了 Maven 插件还有别的生成入口吗? 有:torque-ant-tasks(Ant 构建)与程序内直接调用 torque-generator(写 Java 调 Controller)。注意 4.0 的 generator bin 分发包没有 CLI 启动脚本(实测 MANIFEST 无 Main-Class),命令行生成需要 5.x 及以上版本的分发包。
Q6:多数据库支持怎么做?
torque.properties 里每个库一组键(torque.database.<名>.*),torque.database.default 指定默认库;数据对象 save(String dbName) 与 Peer 的带 Connection 重载可指定目标库。本文单库实测,多库配置结构以官方 runtime 文档为准。
十、参考链接
- Apache Torque 官方主页(组件构成与版本说明):https://db.apache.org/torque/
- Maven 中央仓库 torque-runtime 4.0:https://repo1.maven.org/maven2/org/apache/torque/torque-runtime/4.0/
- Maven 中央仓库 torque-templates 4.0(OM/SQL 模板包):https://repo1.maven.org/maven2/org/apache/torque/torque-templates/4.0/
- Maven 中央仓库 torque-maven-plugin 4.0(含 sources jar):https://repo1.maven.org/maven2/org/apache/torque/torque-maven-plugin/4.0/
- Maven 中央仓库 torque-generator 4.0(含 bin 分发包):https://repo1.maven.org/maven2/org/apache/torque/torque-generator/4.0/
- Apache Derby 官方站点:https://db.apache.org/derby/
- 同系列实操文档:《Ubuntu 22.04 安装 Apache Torque 指南》《CentOS 8 安装 Apache Torque 指南》(工程全文与端到端回显)