`

gae 数据存储区 的 事务说明

阅读更多

事务

App Engine 数据存储区支持事务。事务是一项操作或一系列操作,要么全部成功,要么全部失败。应用程序可以在单个事务中执行多个操作和计算。

使用事务

事务是一项或一系列数据存储区操作,这些操作要么全部成功,要么全部失败。如果事务成功完成,则会对数据存储区产生所有预期的作用。如果事务失败,则不会起任何作用。

每个数据存储区写入操作都是原子操作。要么试图创建、更新或删除实体,要么不执行。如果有太多用户试图同时修改一个实体,那么这种高占用率将可能引发操作失败。当应用程序达到配额限制时,也可能会引发操作失败。数据存储区内部错误也是引发操作失败的原因。在上述所有情况下,操作将不起作用,且数据存储区 API 将引发异常。

以下是一个使用 JDO 事务 API 递增名为 counter 的字段的示例,该字段位于名为 ClubMembers 的对象(未显示类)中:

import javax.jdo.Transaction;

import ClubMembers;   // not shown

// ...
        // PersistenceManager pm = ...;

        Transaction tx = pm.currentTransaction();

        try {
            tx.begin();
    
            ClubMembers members = pm.getObjectById(ClubMembers.class, "k12345");
            members.incrementCounterBy(1);
            pm.makePersistent(members);
    
            tx.commit();
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }

实体组

每个实体都属于一个实体组,它是可以在一个事务中控制的一组实体(一个或多个)。实体组关系会让 App Engine 在分布式网络的相同部分中存储若干实体。事务会针对实体组设置数据存储区操作,且所有操作都会以组的形式应用。如果事务失败,则全都不应用。

当应用程序创建一个实体时,它将另一个实体分配为新实体的父实体。向新实体分配父实体会将新实体放置在与父实体相同的实体组。

没有父实体的实体是根实体。作为另一个实体的父实体的实体也可以有父实体。从某实体到根的父实体链是该实体的路径,路径的成员是该实体的祖先。实体的父实体是在创建该实体时定义的,且以后不能再更改。

每个采用指定根实体作为祖先的实体都在相同的实体组中。一个组中的所有实体都存储在相同的数据存储区节点中。一个事务可以修改一个组中的多个实体,或向组添加新实体(方法是以组中的现有实体作为新实体的父实体)。

使用实体组创建实体

JDO 接口的 App Engine 实现使用实体组表示有主的一对一或一对多关系。这使得对某一个对象的更改以及对其子对象的更改可在同一事务中发生。请参阅关系

对于其他情况,可通过将对象的主键字段设置为完整键(包括父实体的键),显式地使用父实体组来创建实体。对象的主键字段必须是 Key 实例或编码的键字符串(而不是简单的 Long 或 String 类型的键名称)。

当使用应用程序分配的字符串 ID 时,可通过将实体的键字段设置为完整键值(包括父实体的键)使用父实体组来创建对象。要使用父实体组来创建键值,可使用 KeyFactory.Builder 类,如下所示:

import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class AccountInfo {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    public void setKey(Key key) {
        this.key = key;
    }
}

// ...
        KeyFactory.Builder keyBuilder = new KeyFactory.Builder(Customer.class.getSimpleName(), "custid985135");
        keyBuilder.addChild(AccountInfo.class.getSimpleName(), "acctidX142516");
        Key key = keyBuilder.getKey();

        AccountInfo acct = new AccountInfo();
        acct.setKey(key);
        pm.makePersistent(acct);

可使用一个字段与对象的键分开访问父实体组键,如下所示:

// ...
    @Persistent
    @Extension(vendorName="datanucleus", key="gae.parent-pk", value="true")
    private Key customerKey;

要使用系统生成的数字 ID 和父实体组创建对象,则必须使用父实体组键字段(如 customerKey,见上文)。将父实体的键分配到父键字段,然后保留将对象的键字段设置为 null。保存对象时,数据存储区将使用完整键(包括父实体组)填充键字段。

如果一个类具有父实体组字段,则您可在对父字段的查询中使用相等过滤条件。(不支持父键上的不等过滤条件。)

事务的功能

数据存储区对单个事务中可完成的功能施加了许多限制。

事务中的所有数据存储区操作必须在同一实体组中的实体上进行。这包括通过键、更新实体和删除实体来检索实体。请注意,每个根实体都属于单独的实体组,因此,单个事务不能创建多个根实体或在多个根实体上进行操作。

应用程序在事务过程中不能执行查询。但是,应用程序可以在事务过程中使用键检索数据存储区实体,并保证抓取的实体与事务的其余实体一致。您可以在事务之前准备键,或者在事务内部根据键名或 ID 生成键。

应用程序不能在单个事务中多次创建或更新实体。

JDO 将在单个事务中执行调用 tx.begin() 与调用 tx.commit() 之间的所有操作。如果某个操作因所请求的实体组正在被其他进程使用而失败,则 JDO 将引发 JDODataStoreException 或 JDOException,由 java.util.ConcurrentModificationException 引起。

在使用开放式并发的系统中,通常应用程序要在放弃之前多次尝试事务。JDO 仅执行事务一次;应用程序必须根据需要重复事务。例如:

        for (int i = 0; i < NUM_RETRIES; i++) {
            pm.currentTransaction().begin();

            ClubMembers members = pm.getObjectById(ClubMembers.class, "k12345");
            members.incrementCounterBy(1);

            try {
                pm.currentTransaction().commit();
                break;

            } catch (JDOCanRetryException ex) {
                if (i == (NUM_RETRIES - 1)) { 
                    throw ex;
                }
            }
        }

试图在同一事务中更新多个实体组将引发 JDOFatalUserException。请注意,每个没有父实体组的对象都驻留在其自己的实体组中,因此无法在单个事务中创建多个无父实体。

试图在单个事务中多次更新同一实体(如通过重复调用 makePersistent())将引发 JDOFatalUserException。而应在事务中只修改持久对象,并允许调用 commit() 以应用更改。

事务的用途

该示例说明了事务的一个用途:使用与属性当前值相关的新属性值更新实体。

        Key k = KeyFactory.createKey("Employee", "k12345");
        Employee e = pm.getObjectById(Employee.class, k);
        e.counter += 1;
        pm.makePersistent(e);

这需要使用事务,因为在此代码抓取对象之后和保存修改的对象之前,其他用户可能会更新值。如果不使用事务,该用户的请求将在其他用户的更新前使用 counter 的值,并且保存时将覆盖此新值。如果使用事务,应用程序将得知其他用户的更新。 如果实体在事务期间进行了更新,则该事务将失败,同时引发异常。应用程序可重复事务以使用新数据。

事务的另一个常见用途,是使用命名的键更新实体,或当实体不存在时创建实体:

        // PersistenceManager pm = ...;

        Transaction tx = pm.currentTransaction();

        String id = "jj_industrial";
        String companyName = "J.J. Industrial";
       
        try {
            tx.begin();

            Key k = KeyFactory.createKey("SalesAccount", id);
            SalesAccount account;
            try {
                account = pm.getObjectById(Employee.class, k);
            } catch (JDOObjectNotFoundException e) {
                account = new SalesAccount();
                account.setId(id);
            }

            account.setCompanyName(companyName);
            pm.makePersistent(account);

            tx.commit();

        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }

同以前一样,如果其他用户尝试使用相同的字符串 ID 创建或更新实体,则需要使用事务来处理这种情况。在不使用事务的情况下,如果实体不存在,且两个用户都尝试创建该实体,第二个用户将覆盖第一个用户的实体,且不知道发生了此情况。 如果使用事务,第二个用户的尝试会发生原子失败,并且可由应用程序重试,以抓取新实体并进行更新。

提示:应当尽可能快地执行事务,以减少该事务所用实体被更改从而需要重试该事务的可能性。在该事务以外尽可能多地准备数据,然后执行该事务的数据存储区操作,该操作要求数据处于稳定状态。应用程序应该为事务内部所使用的对象准备键,然后抓取事务内部的实体。

禁用事务和传输现有的 JDO App

我们推荐使用的 JDO 配置将名为 datanucleus.appengine.autoCreateDatastoreTxns 的属性设置为 true。这是 App Engine 特定的属性,它告知 JDO 实现将数据存储区事务与在应用程序代码中管理的 JDO 事务相关联。如果您要从头开始构建一个新应用程序,这可能是您应采用的做法。但是,如果您已经有了一个将要在 App Engine 上运行的基于 JDO 的应用程序,则可能要使用其他持久配置,将该属性值设置为 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.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
        <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 数据存储区不支持全局事务,所以使用全局事务的代码会引发异常。您只要禁用数据存储区事务即可,而不需要浏览整个代码库(可能很大)并删除所有事务管理代码。当然,这不能证实代码对多记录修改的原子性所作的假设,但是可使应用程序正常工作,这样您便可以根据需要专注于逐步重构事务代码,而不是一次性完成。

分享到:
评论

相关推荐

    hegira-generator:为远程 GAE 数据存储实例生成实体

    数据生成是通过 CPIM 和 Kundera GAE 数据存储扩展提供的。 ###Bulild 只需运行: mvn clean package 创建了一个可执行的 jar。 请注意,pom 文件包含的修改版本和作为依赖项,这些在 maven 的公共存储库中不...

    基于GAE的Demo

    GAE是Google提供的一个云计算平台,允许开发者在Google的基础设施上部署和运行Web应用,提供了包括计算、存储、数据库等在内的多种服务。 首先,Google App Engine(GAE)是Google推出的一个PaaS(Platform as a ...

    dsql:数据存储区SQL。 适用于Google App Engine数据存储区SQL引擎

    DSQL扩展了gae数据存储区,以实现各种数据存储区之间的本地联接,以及为未索引的属性提供本地过滤。 DSQL使用熟悉的sql语法。 SQL联接使用数据存储统计信息进行了优化。 假定数据存储架构的设计类似于关系数据库。...

    使用 Bigtable、Blobstore 和 Google Storage 实现 GAE 存储.doc

    - `Photo` Bean:定义了存储图像所需的信息,如标题、说明和二进制数据。 - Datastore DAO:使用Objectify库来与Bigtable交互,实现存储和检索`Photo`对象。 - Servlet:采用Template Method模式封装工作流程,对每...

    spring+gae

    2. **数据存储**:Spring与GAE的数据存储API(如JDO或JPA)结合,可以提供一个统一的数据访问层,简化对Google Datastore的操作。 3. **调度任务**:Spring的TaskExecution和TaskScheduler模块可以与GAE的后台任务...

    GAE--java使用入门.doc

    **App Engine Java SDK** 是开发GAE Java应用的核心工具,它包含了运行和测试应用所需的服务器软件,以及模拟所有GAE服务的本地版本,如**数据存储区**、**Google账户集成**和API调用。SDK还允许你在本地计算机上...

    gae-pytorch-master_pytorch_pytorchgae_GAE_自编码器_gaepytorchmaster_

    【标题】"gae-pytorch-master_pytorch_pytorchgae_GAE_自编码器_gaepytorchmaster_" 提供的信息表明,这是一个使用PyTorch实现的图自编码器(Graph Autoencoder, GAE)项目,其核心是将自编码器的概念应用于图数据。...

    GAE编程指南

    《GAE编程指南》是一种云计算服务,跟其他的同类产品不同,它提供了一种简单的应用程序...你将会学到有关App Engine的应用程序服务器架构、运行时环境以及可伸缩数据存储区等知识,还可以学到一些应用程序优化方法。

    GAE使用规则

    GAE使用规则GAE使用规则GAE使用规则GAE使用规则GAE使用规则GAE使用规则GAE使用规则GAE使用规则GAE使用规则

    gae编程指南

    《GAE编程指南》是一种云计算服务,跟其他的同类产品不同,它提供了一种简单的应用程序...你将会学到有关App Engine的应用程序服务器架构、运行时环境以及可伸缩数据存储区等知识,还可以学到一些应用程序优化方法。

    GAE入门教程

    pass之GAE入门教程, 学习GAE

    gae_in_pytorch-master_GAE_

    图形自动编码器(Graph Autoencoder, GAE)是一种应用于图数据的深度学习模型,它结合了自动编码器(Autoencoder)的思想与图神经网络(Graph Neural Network, GNN)的特性。GAE的目标是学习到图的低维表示,即节点...

    GAE入门教程.pdf

    - GAE 使用自己的数据存储系统,称为 Datastore,提供了 NoSQL 数据模型。 - 查询语言 GQL 类似于 SQL,用于检索和操作数据。 8. **使用模板文件** - Django 模板引擎可以用于创建动态网页,实现数据和页面结构...

    GAE之webapp框架

    ### GAE之webapp框架详解 #### 一、引言 在Google App Engine (GAE) 平台上进行Web应用开发时,选择合适的框架对于提高开发效率至关重要。其中,`webapp` 框架因其简洁高效而备受开发者青睐。本篇文章将详细介绍`...

    GAE blog安装

    2. **项目结构**:在GAE上部署应用通常需要一个特定的项目结构,包括`app.yaml`配置文件,它定义了应用的运行环境、服务、版本和其他元数据。在给定的文件名称列表中,我们看到了`index.yaml`,这通常是用于定义应用...

    GAE包(以配置好,解压可用)

    标题 "GAE包(以配置好,解压可用)" 提供的信息表明,这是一个已经预配置好的Google App Engine (GAE)开发环境的压缩包。GAE是Google提供的一项平台即服务(PaaS),允许开发者在Google的基础设施上运行自己的Web...

    spring+gae+hibernate

    Hibernate作为ORM工具,可以将Java对象与GAE的数据存储服务(如Datastore)进行对接,简化数据库操作。GAE则为这个应用提供了弹性云环境,自动处理负载和扩展需求。 在实际的集成过程中,可能涉及到以下步骤: 1. ...

    关于GAE的教程、工具与文档(建站)

    3. **数据存储**:GAE提供了NoSQL数据库服务,称为**Datastore**。它是一种非关系型分布式数据库,支持高可用性和可扩展性。了解其数据模型(实体、键、属性)和查询语言(GQL或通过API)是至关重要的。 4. **服务...

Global site tag (gtag.js) - Google Analytics