Apache OpenJPA 3.2.2 构建并运行示例应用指南
Apache OpenJPA 3.2.2 构建并运行示例应用指南(JPA 持久化应用示例):从零创建 Maven 工程、完整 pom.xml 与 persistence.xml、实体类与 CRUD 主类源码、构建期增强、编译运行命令与真实回显(H2 内存库新增 3 条/条件查询/聚合/删除,SQL=TRACE 建表与增删改查日志),含 JVM 堆限制验证。
Apache OpenJPA 3.2.2 构建并运行示例应用指南
目标:从零构建并运行一个基于 Apache OpenJPA 3.2.2 的最小 JPA 持久化应用(实体映射 + CRUD + JPQL)。给出完整 pom.xml、persistence.xml、实体类与主类源码,编译(含构建期增强)与运行命令及真实回显:H2 内存库自动建表、新增 3 条员工记录、JPQL 条件查询研发部 2 条、聚合平均薪资 20000.0、删除后总数 2。所有命令与输出均在 Ubuntu 22.04.5 LTS + JDK 11 + OpenJPA 3.2.2 环境实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS
- Java:OpenJDK 11.0.32.1
- 构建工具:Apache Maven 3.6.3
- OpenJPA 版本:3.2.2(依赖 org.apache.openjpa:openjpa)
- 数据库:H2 2.2.224(内存库
jdbc:h2:mem:testdb) - 示例工程目录:
/root/openjpa-demo/ - 演示账号:root
二、前置条件
-
已按《Ubuntu 22.04 安装 Apache OpenJPA 3.2.2 指南》安装 JDK 11 与 Maven:
java -version mvn -version -
Java 版本回显需为 11(
openjdk version "11.0.32.1" 2026-08-18)。
三、工程结构
标准 Maven 布局:
openjpa-demo/
├── pom.xml
└── src/main/
├── java/demo/
│ ├── Employee.java
│ └── Main.java
└── resources/META-INF/
└── persistence.xml
mkdir -p /root/openjpa-demo/src/main/java/demo
mkdir -p /root/openjpa-demo/src/main/resources/META-INF
四、完整 pom.xml
pom 引入 org.apache.openjpa:openjpa:3.2.2 与 com.h2database:h2:2.2.224,并通过 openjpa-maven-plugin 在 process-classes 阶段自动增强实体类:
<?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>openjpa-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<openjpa.version>3.2.2</openjpa.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa</artifactId>
<version>${openjpa.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-maven-plugin</artifactId>
<version>${openjpa.version}</version>
<configuration>
<includes>demo/Employee.class</includes>
</configuration>
<executions>
<execution>
<id>enhancer</id>
<phase>process-classes</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
</project>
⚠️ 注意:
openjpa-maven-plugin的enhance目标绑定process-classes阶段,mvn compile不会触发,必须用mvn process-classes/mvn package。
依赖解析结果(mvn dependency:tree):
[INFO] demo:openjpa-demo:jar:1.0-SNAPSHOT
[INFO] +- org.apache.openjpa:openjpa:jar:3.2.2:compile
[INFO] | +- net.sourceforge.serp:serp:jar:1.15.1:compile
[INFO] | +- org.apache.geronimo.specs:geronimo-jms_1.1_spec:jar:1.1.1:compile
[INFO] | +- org.apache.geronimo.specs:geronimo-jta_1.1_spec:jar:1.1.1:compile
[INFO] | +- org.apache.commons:commons-pool2:jar:2.9.0:compile
[INFO] | +- org.apache.xbean:xbean-asm9-shaded:jar:4.20:compile
[INFO] | \- org.apache.geronimo.specs:geronimo-jpa_2.2_spec:jar:1.1:compile
[INFO] +- com.h2database:h2:jar:2.2.224:compile
五、persistence.xml(持久化单元配置)
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
version="2.2">
<persistence-unit name="openjpa-demo">
<class>demo.Employee</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="openjpa.jdbc.DBDictionary" value="h2"/>
<property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO, SQL=TRACE"/>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema"/>
</properties>
</persistence-unit>
</persistence>
要点:
- 持久化单元名
openjpa-demo,实体类demo.Employee显式声明; openjpa.jdbc.SynchronizeMappings=buildSchema:启动时自动建表(开发/演示用);openjpa.Log的SQL=TRACE:控制台打印实际执行的 SQL。
六、实体类(Employee.java)
package demo;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(nullable = false, length = 64)
private String name;
@Column(length = 32)
private String department;
private double salary;
public Employee() {
}
public Employee(String name, String department, double salary) {
this.name = name;
this.department = department;
this.salary = salary;
}
public long getId() { return id; }
public void setId(long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
public double getSalary() { return salary; }
public void setSalary(double salary) { this.salary = salary; }
@Override
public String toString() {
return String.format("Employee[id=%d, name=%s, department=%s, salary=%.2f]",
id, name, department, salary);
}
}
七、主类(Main.java,完整 CRUD)
package demo;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
public class Main {
public static void main(String[] args) {
// 1. 创建 EntityManagerFactory(读取 META-INF/persistence.xml 的 openjpa-demo 持久化单元)
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("openjpa-demo");
System.out.println("== EntityManagerFactory 创建成功 ==");
// 2. 新增(CREATE)
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Employee e1 = new Employee("张三", "研发部", 18000.0);
Employee e2 = new Employee("李四", "市场部", 15000.0);
Employee e3 = new Employee("王五", "研发部", 22000.0);
em.persist(e1);
em.persist(e2);
em.persist(e3);
tx.commit();
System.out.println("== 新增完成: 3 名员工已提交 ==");
// 3. 查询(READ):JPQL 全量
TypedQuery<Employee> allQuery =
em.createQuery("select e from Employee e order by e.id", Employee.class);
List<Employee> all = allQuery.getResultList();
System.out.println("== JPQL 查询全部员工 (" + all.size() + " 条) ==");
for (Employee e : all) {
System.out.println(" " + e);
}
// 4. 更新(UPDATE):李四调岗加薪
tx = em.getTransaction();
tx.begin();
Employee e2Managed = em.find(Employee.class, e2.getId());
e2Managed.setDepartment("运营部");
e2Managed.setSalary(16000.0);
tx.commit();
System.out.println("== 更新完成: " + e2Managed.getId() + " 号员工部门/薪资已修改 ==");
// 5. JPQL 带条件查询 + 聚合
TypedQuery<Employee> devQuery = em.createQuery(
"select e from Employee e where e.department = :dept order by e.salary desc",
Employee.class);
devQuery.setParameter("dept", "研发部");
List<Employee> devs = devQuery.getResultList();
System.out.println("== JPQL 条件查询 研发部 (" + devs.size() + " 条) ==");
for (Employee e : devs) {
System.out.println(" " + e);
}
Query avgQuery = em.createQuery(
"select avg(e.salary) from Employee e where e.department = :dept");
avgQuery.setParameter("dept", "研发部");
Object avg = avgQuery.getSingleResult();
System.out.println("== JPQL 聚合查询 研发部平均薪资: " + avg + " ==");
// 6. 删除(DELETE)
tx = em.getTransaction();
tx.begin();
Employee e3Managed = em.find(Employee.class, e3.getId());
em.remove(e3Managed);
tx.commit();
System.out.println("== 删除完成: " + e3.getId() + " 号员工已删除 ==");
long count = (Long) em.createQuery("select count(e) from Employee e").getSingleResult();
System.out.println("== 删除后员工总数: " + count + " ==");
em.close();
emf.close();
System.out.println("== 全部 CRUD 操作执行完毕 ==");
}
}
八、构建与运行
8.1 构建(编译 + 实体增强)
cd /root/openjpa-demo
mvn -q clean process-classes
实测回显(openjpa-maven-plugin 在 process-classes 阶段增强 demo.Employee):
79 openjpa-demo INFO [main] openjpa.Tool - Enhancer running on type "class demo.Employee".
验证增强结果:
javap -p target/classes/demo/Employee.class | head -5
实测回显:
Compiled from "Employee.java"
public class demo.Employee implements org.apache.openjpa.enhance.PersistenceCapable {
private long id;
private java.lang.String name;
private java.lang.String department;
8.2 生成 classpath 并运行
mvn -q dependency:build-classpath -Dmdep.outputFile=/tmp/cp.txt
java -Xms256m -Xmx256m -cp "target/classes:$(cat /tmp/cp.txt)" demo.Main
实测回显(完整运行输出):
== EntityManagerFactory 创建成功 ==
== 新增完成: 3 名员工已提交 ==
== JPQL 查询全部员工 (3 条) ==
Employee[id=1, name=张三, department=研发部, salary=18000.00]
Employee[id=2, name=李四, department=市场部, salary=15000.00]
Employee[id=3, name=王五, department=研发部, salary=22000.00]
== 更新完成: 2 号员工部门/薪资已修改 ==
== JPQL 条件查询 研发部 (2 条) ==
Employee[id=3, name=王五, department=研发部, salary=22000.00]
Employee[id=1, name=张三, department=研发部, salary=18000.00]
== JPQL 聚合查询 研发部平均薪资: 20000.0 ==
== 删除完成: 3 号员工已删除 ==
== 删除后员工总数: 2 ==
== 全部 CRUD 操作执行完毕 ==
8.3 验证 JVM 堆参数生效
运行期间检查进程参数:
ps -ef | grep "[j]ava" | grep -oE "java -Xms[^ ]+ -Xmx[^ ]+|demo.Main"
实测回显:
java -Xms256m -Xmx256m
demo.Main
九、SQL 日志解读(SQL=TRACE)
开启 openjpa.Log 的 SQL=TRACE 后,控制台打印 OpenJPA 生成的 SQL(节选):
949 openjpa-demo TRACE [demo.Main.main()] openjpa.jdbc.SQL - <t 751936279, conn 1551666102> executing stmnt 1318924920 CREATE TABLE employees (id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, department VARCHAR(32), name VARCHAR(64) NOT NULL, salary DOUBLE, PRIMARY KEY (id))
1115 openjpa-demo TRACE [demo.Main.main()] openjpa.jdbc.SQL - <t 751936279, conn 1417561088> executing prepstmnt 1702493641 INSERT INTO employees (department, name, salary) VALUES (?, ?, ?) [params=?, ?, ?]
1212 openjpa-demo TRACE [demo.Main.main()] openjpa.jdbc.SQL - <t 751936279, conn 1476229122> executing prepstmnt 379916524 SELECT t0.id, t0.department, t0.name, t0.salary FROM employees t0 ORDER BY t0.id ASC
1247 openjpa-demo TRACE [demo.Main.main()] openjpa.jdbc.SQL - <t 751936279, conn 1890934476> executing prepstmnt 1436583577 UPDATE employees SET department = ?, salary = ? WHERE id = ? [params=?, ?, ?]
1290 openjpa-demo TRACE [demo.Main.main()] openjpa.jdbc.SQL - <t 751936279, conn 1553697462> executing prepstmnt 1217883989 SELECT AVG(t0.salary) FROM employees t0 WHERE (t0.department = ?) [params=?]
1305 openjpa-demo TRACE [demo.Main.main()] openjpa.jdbc.SQL - <t 751936279, conn 480540393> executing prepstmnt 602412335 DELETE FROM employees WHERE id = ? [params=?]
1318 openjpa-demo TRACE [demo.Main.main()] openjpa.jdbc.SQL - <t 751936279, conn 1497263599> executing prepstmnt 1134173540 SELECT COUNT(t0.id) FROM employees t0
对应关系:JPQL 全量查询 → SELECT t0.* FROM employees t0 ORDER BY t0.id ASC;条件查询 → WHERE (t0.department = ?) ORDER BY t0.salary DESC;聚合 → SELECT AVG(t0.salary) ...。SQL 中的 t0 为实体别名,? 为预编译参数。
十、常见问题
Q1:运行报 Table "EMPLOYEES" not found?
A:未开启自动建表或实体未增强。确认 persistence.xml 含 openjpa.jdbc.SynchronizeMappings=buildSchema,并已执行 mvn clean process-classes 完成增强。
Q2:JPQL 查询返回结果与数据库不一致?
A:内存库 jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 与进程生命周期一致;多进程/重启后数据不复存在,属正常现象。生产请改用文件数据库(H2 文件模式 jdbc:h2:/opt/data/testdb 或 Derby 文件模式)。
Q3:如何切换到底层数据库 Derby?
A:pom 增加 org.apache.derby:derby:10.14.2.0 依赖,persistence.xml 修改为:
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:derby:memory:testdb;create=true"/>
<property name="openjpa.jdbc.DBDictionary" value="derby"/>
实测同一程序在 Derby 上完整 CRUD 全部通过(3 条插入/查询/更新/聚合/删除,回显与 H2 相同)。
Q4:EntityManager 或 EntityManagerFactory 用完不关闭会怎样?
A:连接与缓存不释放。务必在 finally 或 try-with-resources 中调用 em.close()、emf.close()。
十一、参考链接
- Apache OpenJPA 项目主页:https://openjpa.apache.org/
- OpenJPA 官方文档:https://openjpa.apache.org/documentation.html
- JPA 2.2 规范:https://jcp.org/en/jsr/detail?id=338