Java 数据对象 (JDO) 是访问 Java 数据库的标准接口,提供 Java 类和数据库表之间的映射。将 JDO 与数据存储区搭配使用需要借助一种开源插件,本页面介绍了如何开始使用该插件。
警告:我们认为,对于大多数开发者来说,使用低层级的 Datastore API 或某种专为 Datastore 开发的开源 API(如 Objectify)会获得更好的体验。 JDO 专为与传统关系型数据库搭配使用而设计,因此无法明确表示数据存储区不同于关系型数据库的一些方面,例如实体组和祖先查询。 这可能会导致难以理解和解决的细微问题。
App Engine Java SDK 包含 2.x 版 App Engine DataNucleus 插件。此插件的作用相当于 3.0 版 DataNucleus Access Platform,让您能够通过 JDO 3.0 使用 App Engine Datastore。
如需详细了解 JDO,请参阅 Access Platform 3.0 文档。请特别留意 JDO 映射和 JDO API 部分。
警告:2.x 版 App Engine DataNucleus 插件使用 DataNucleus v3.x。此新版插件与旧版 (1.x) 插件不完全向后兼容。如果要升级到新版本,请务必更新并测试您的应用。
支持 JDO 2.x 和 3.0 的编译工具
您可以通过 Maven 来使用 App Engine 的 2.x 或 3.0 版 DataNucleus 插件:
- 对于 Maven 用户:您可以在
pom.xml
文件中使用以下配置来增强类:<plugin> <groupId>org.datanucleus</groupId> <artifactId>maven-datanucleus-plugin</artifactId> <version>3.2.0-m1</version> <configuration> <api>JDO</api> <props>${basedir}/datanucleus.properties</props> <verbose>true</verbose> <enhancerName>ASM</enhancerName> </configuration> <executions> <execution> <phase>process-classes</phase> <goals> <goal>enhance</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.datanucleus</groupId> <artifactId>datanucleus-core</artifactId> <version>3.1.3</version> </dependency> </dependencies> </plugin>
迁移到 2.x 版 DataNucleus 插件
本部分说明如何将应用升级为使用 2.x 版 App Engine DataNucleus 插件,该插件的作用相当于 DataNucleus Access Platform 3.0 和 JDO 3.0。该插件与 1.x 版插件不完全向后兼容,且可能会发生更改。如果要升级到新版本,请务必更新并测试应用代码。
新版默认行为
2.x 版 App Engine DataNucleus 插件与之前的 1.x 版之间在一些默认值方面有所不同:
- 现在,以原子方式执行对
PersistenceManager.makePersistent()
和PersistenceManager.deletePersistent()
的非事务性调用。(以前,这些函数在下一个事务中执行,或者在PersistenceManager.close()
之后执行。) - 现在,重复的 PersistenceManagerFactory (PMF) 分配不会再引发异常。相反,如果您将持久性属性
datanucleus.singletonPMFForName
设置为 truetrue,则该属性会返回当前为该名称分配的单例 PMF。 - 现在支持无主关系。请参阅无主关系。
配置文件变更
如要将应用程序升级到 App Engine DataNucleus 插件的 2.x 版,您需要更改 build.xml
和 jdoconfig.xml
中的配置设置。
警告!更新配置后,您需要测试应用代码,以确保向后兼容性。如果您要设置新的应用并使用最新版插件,请继续设置 JDO 3.0。
PersistenceManagerFactoryClass
属性已更改。在jdoconfig.xml
中将此行:<property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
更改为:
<property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
在 build.xml
中
要适应 DataNucleus 2.x,需要更改 copyjars
目标:
copyjars
目标已更改。将此部分: 更新为:<target name="copyjars" description="Copies the App Engine JARs to the WAR."> <mkdir dir="war/WEB-INF/lib" /> <copy todir="war/WEB-INF/lib" flatten="true"> <fileset dir="${sdk.dir}/lib/user"> <include name="**/*.jar" /> </fileset> </copy> </target>
<target name="copyjars" description="Copies the App Engine JARs to the WAR."> <mkdir dir="war/WEB-INF/lib" /> <copy todir="war/WEB-INF/lib" flatten="true"> <fileset dir="${sdk.dir}/lib/user"> <include name="**/appengine-api-1.0-sdk*.jar" /> </fileset> <fileset dir="${sdk.dir}/lib/opt/user"> <include name="appengine-api-labs/v1/*.jar" /> <include name="jsr107/v1/*.jar" /> <include name="datanucleus/v2/*.jar" /> </fileset> </copy> </target>
datanucleusenhance
目标已更改。将此部分: 更新为:<target name="datanucleusenhance" depends="compile" description="Performs enhancement on compiled data classes."> <enhance_war war="war" /> </target>
<target name="datanucleusenhance" depends="compile" description="Performs enhancement on compiled data classes."> <enhance_war war="war"> <args> <arg value="-enhancerVersion"/> <arg value="v2"/> </args> </enhance_war> </target>
设置 JDO 3.0
要使用 JDO 访问数据存储区,App Engine 应用需要满足以下条件:
- JDO 和 DataNucleus 插件的 JAR 必须位于应用的
war/WEB-INF/lib/
目录中。 - 名为
jdoconfig.xml
的配置文件必须位于应用的war/WEB-INF/classes/META-INF/
目录中,该文件包含告诉 JDO 使用 App Engine 数据存储区的配置。 - 项目的构建过程必须对已编译的数据类执行编译后“增强”步骤,以将这些其与 JDO 实现关联。
复制 JAR
JDO 和数据存储区 JAR 都包含在 App Engine Java SDK 中。您可以在 appengine-java-sdk/lib/opt/user/datanucleus/v2/
目录中找到它们。
将 JAR 复制到您的应用的 war/WEB-INF/lib/
目录中。
确保 appengine-api.jar
也在 war/WEB-INF/lib/
目录中。(创建项目时您可能已经复制了此文件。)App Engine DataNucleus 插件使用此 JAR 来访问数据存储区。
创建 jdoconfig.xml 文件
JDO 接口需要应用的 war/WEB-INF/classes/META-INF/
目录中名为 jdoconfig.xml
的配置文件。您可以直接在此位置中创建该文件,也可以让构建进程从源目录中复制该文件。
请使用以下内容创建该文件:
<?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> <property name="datanucleus.appengine.singletonPMFForName" value="true"/> </persistence-manager-factory> </jdoconfig>
设置 Datastore 读取政策和最后调用期限
如 Datastore 查询页面所述,您可以通过设置读取政策(高度一致性与最终一致性)和调用时限自定义 Datastore 的行为。在 JDO 中,您可以通过在 jdoconfig.xml
文件的 <persistence-manager-factory>
元素中指定所需的值来实现此目的。借助给定的 PersistenceManager
实例执行的所有调用都将使用 PersistenceManagerFactory
创建管理器时生效的配置值。您还可以为单个 Query
对象替换这些设置。
要为 PersistenceManagerFactory
设置读取策略,请包含一个名为 datanucleus.appengine.datastoreReadConsistency
的属性。可能值为 EVENTUAL
和 STRONG
:如果未指定值,则默认值为 STRONG
。(但请注意,这些设置仅适用于给定实体组内的祖先查询。非祖先查询始终具备最终一致性,无论主导性的读取政策为何。)
<property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />
您可以为读取和写入分别设置数据存储区调用期限。对于读取,请使用 JDO 标准属性 javax.jdo.option.DatastoreReadTimeoutMillis
。对于写入,请使用 javax.jdo.option.DatastoreWriteTimeoutMillis
。该值为时间量,以毫秒为单位。
<property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" /> <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" />
如果您要使用跨组 (XG) 事务,请添加以下属性:
<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />
借助不同的 name
特性,您可以在同一个 jdoconfig.xml
文件中拥有多个 <persistence-manager-factory>
元素,从而可在同一个应用中使用具有不同配置的 PersistenceManager
实例。例如,以下 jdoconfig.xml
文件建立了两组配置,一组名为 "transactions-optional"
,另一组名为 "eventual-reads-short-deadlines"
:
<?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> </persistence-manager-factory> <persistence-manager-factory name="eventual-reads-short-deadlines"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" /> <property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" /> <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" /> <property name="datanucleus.singletonPMFForName" value="true" /> </persistence-manager-factory> </jdoconfig>
如需了解如何使用指定配置集创建 PersistenceManager
,请参阅下面的获取 PersistenceManager 实例。
增强数据类
JDO 在构建过程中使用编译后“增强”步骤,以便将数据类与 JDO 关联。
您可以从命令行使用以下命令对已编译的类执行增强步骤:
java -cp classpath com.google.appengine.tools.enhancer.Enhance class-files
classpath类路径必须包含来自 appengine-java-sdk/lib/
目录的 JAR appengine-tools-api.jar
,以及您的所有数据类。
如需详细了解 DataNucleus 字节码增强器,请参阅 DataNucleus 文档。
获取 PersistenceManager 实例
应用使用 PersistenceManager 类的实例与 JDO 进行交互。 通过在 PersistenceManagerFactory 类的实例上实例化并调用一个方法,您可以获取此实例。 该工厂使用 JDO 配置来创建 PersistenceManager 实例。
由于对 PersistenceManagerFactory 实例进行初始化需要一些时间,因此应用应重用单个实例。管理 PersistenceManagerFactory 实例的一个简便方法是使用静态实例创建单例封装容器类,如下所示:
PMF.java
import javax.jdo.JDOHelper; import javax.jdo.PersistenceManagerFactory; public final class PMF { private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional"); private PMF() {} public static PersistenceManagerFactory get() { return pmfInstance; } }
提示:"transactions-optional"
指的是 jdoconfig.xml
文件中的配置集的名称。如果您的应用使用多个配置集,则必须根据需要扩展此代码来调用 JDOHelper.getPersistenceManagerFactory()
。您的代码应该缓存每个 PersistenceManagerFactory
的单例实例。
该应用使用工厂实例为访问数据存储区的每个请求创建一个 PersistenceManager 实例。
import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import PMF; // ... PersistenceManager pm = PMF.get().getPersistenceManager();
您可以使用 PersistenceManager 来存储、更新和删除数据对象,并执行数据存储区查询。
使用 PersistenceManager 实例之后,必须调用该实例的 close()
方法。在调用 PersistenceManager 实例的 close()
方法之后使用该实例是一种错误做法。
try { // ... do stuff with pm ... } finally { pm.close(); }
不受支持的 JDO 3.0 功能
JDO 接口的以下功能不受 App Engine 实现的支持:
- 有主的多对多关系。
- “Join”查询。对父类型执行查询时,您不能在过滤条件中使用子实体的字段。请注意,您可以直接在查询中使用某个键来测试父类型的关系字段。
- JDOQL 分组和其他聚合查询。
- 多态查询。您不能通过对某个类执行查询来获取子类的实例。在数据存储区中,每个类都是由一个单独的实体种类表示的。
停用事务和移植现有 JDO 应用
我们推荐使用的 JDO 配置将名为 datanucleus.appengine.autoCreateDatastoreTxns
的属性设置为 true
。该属性为 App Engine 专属属性,可指示 JDO 实现将数据存储区事务与应用代码中托管的 JDO 事务关联。 如果您要从头构建新应用,则可能希望实现这一操作。不过,如果您有一个基于 JDO 的现有应用,并且您想在 App Engine 上运行它,那么您可以使用将此属性的值设置为 false
的另一个持久性配置:
<?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="false"/> </persistence-manager-factory> </jdoconfig>
为了理解这样做的好处,请记住,您只能对属于某事务中的同一实体组的对象进行操作。 使用传统数据库构建的应用通常会假定全局事务具备可用性,以便您更新某事务中的任意记录集。 由于 App Engine 数据存储区不支持全局事务,因此,如果您的代码假定全局事务具备可用性,则 App Engine 会抛出异常。您只需停用数据存储区事务,而无需检查(可能很大的)代码库并移除所有事务管理代码。尽管这样做并不能解决您的代码对多记录修改的原子性所作的假设,但可让您的应用正常运行,从而使您够专注于以递增方式重构事务性代码,并且是根据需要进行重构,而不是一次全部完成。