com.google.cloud.datastore.Transaction Maven / Gradle / Ivy
Show all versions of google-cloud-datastore Show documentation
/*
* Copyright 2015 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.cloud.datastore;
import com.google.api.core.BetaApi;
import com.google.cloud.datastore.models.ExplainOptions;
import com.google.protobuf.ByteString;
import java.util.Iterator;
import java.util.List;
import javax.annotation.concurrent.NotThreadSafe;
/**
* A Google cloud datastore transaction. Similar to {@link Batch} any write operation that is
* applied on a transaction will only be sent to the Datastore upon {@link #commit}. A call to
* {@link #rollback} will invalidate the transaction and discard the changes. Any read operation
* that is done by a transaction will be part of it and therefore a {@code commit} is guaranteed to
* fail if an entity was modified outside the transaction after it was read. Write operation on this
* transaction will not be reflected by read operation (as the changes are only sent to the
* Datastore upon {@code commit}. A usage example:
*
* {@code
* Transaction transaction = datastore.newTransaction();
* try {
* Entity entity = transaction.get(key);
* if (!entity.contains("last_name") || entity.isNull("last_name")) {
* String[] name = entity.getString("name").split(" ");
* entity = Entity.newBuilder(entity)
* .remove("name")
* .set("first_name", name[0])
* .set("last_name", name[1])
* .build();
* transaction.update(entity);
* transaction.commit();
* }
* } finally {
* if (transaction.isActive()) {
* transaction.rollback();
* }
* }
* }
*
* @see Google Cloud
* Datastore transactions
* WARNING: This class maintains an internal state in terms of {@link
* java.util.LinkedHashMap} and {@link java.util.LinkedHashSet} which gets updated on every
* method call performing CRUD operations to record the mutations. Since {@link
* java.util.LinkedHashMap} is not thread safe as per its documentation,
* This class too should not be treated as a thread safe class.
*/
@NotThreadSafe
public interface Transaction extends DatastoreBatchWriter, DatastoreReaderWriter {
interface Response {
/** Returns a list of keys generated by a transaction. */
List getGeneratedKeys();
}
/**
* {@inheritDoc} The requested entity will be part of this Datastore transaction (so a commit is
* guaranteed to fail if entity was changed by others after it was seen by this transaction) but
* any write changes in this transaction will not be reflected by the returned entity.
*
* Example of getting an entity for a given key.
*
*
{@code
* String keyName = "my_key_name";
* Key key = datastore.newKeyFactory().setKind("MyKind").newKey(keyName);
* Entity entity = transaction.get(key);
* transaction.commit();
* // Do something with the entity
* }
*
* @throws DatastoreException upon failure or if no longer active
*/
@Override
Entity get(Key key);
/**
* {@inheritDoc} The requested entities will be part of this Datastore transaction (so a commit is
* guaranteed to fail if any of the entities was changed by others after they were seen by this
* transaction) but any write changes in this transaction will not be reflected by the returned
* entities.
*
* Example of getting entities for several keys.
*
*
{@code
* String firstKeyName = "my_first_key_name";
* String secondKeyName = "my_second_key_name";
* KeyFactory keyFactory = datastore.newKeyFactory().setKind("MyKind");
* Key firstKey = keyFactory.newKey(firstKeyName);
* Key secondKey = keyFactory.newKey(secondKeyName);
* Iterator entitiesIterator = transaction.get(firstKey, secondKey);
* List entities = Lists.newArrayList();
* while (entitiesIterator.hasNext()) {
* Entity entity = entitiesIterator.next();
* // do something with the entity
* entities.add(entity);
* }
* transaction.commit();
* }
*
* @throws DatastoreException upon failure or if no longer active
*/
@Override
Iterator get(Key... key);
/**
* {@inheritDoc} The requested entities will be part of this Datastore transaction (so a commit is
* guaranteed to fail if any of the entities was changed by others after they were seen by this
* transaction) but any write changes in this transaction will not be reflected by the returned
* entities.
*
* Example of fetching a list of entities for several keys.
*
*
{@code
* String firstKeyName = "my_first_key_name";
* String secondKeyName = "my_second_key_name";
* KeyFactory keyFactory = datastore.newKeyFactory().setKind("MyKind");
* Key firstKey = keyFactory.newKey(firstKeyName);
* Key secondKey = keyFactory.newKey(secondKeyName);
* List entities = transaction.fetch(firstKey, secondKey);
* for (Entity entity : entities) {
* // do something with the entity
* }
* transaction.commit();
* }
*
* @throws DatastoreException upon failure or if no longer active
*/
@Override
List fetch(Key... keys);
/**
* {@inheritDoc} The entities returned by the result of this query will be part of this Datastore
* transaction (so a commit is guaranteed to fail if any of the entities was changed by others
* after the query was performed) but any write changes in this transaction will not be reflected
* by the result.
*
* Example of running a query to find all entities with an ancestor.
*
*
{@code
* String parentKeyName = "my_parent_key_name";
* KeyFactory keyFactory = datastore.newKeyFactory().setKind("ParentKind");
* Key parentKey = keyFactory.newKey(parentKeyName);
* // Build a query
* Query query = Query.newEntityQueryBuilder()
* .setKind("MyKind")
* .setFilter(PropertyFilter.hasAncestor(parentKey))
* .build();
* QueryResults results = transaction.run(query);
* List entities = Lists.newArrayList();
* while (results.hasNext()) {
* Entity result = results.next();
* // do something with result
* entities.add(result);
* }
* transaction.commit();
* }
*
* @throws DatastoreException upon failure or if no longer active
*/
@Override
QueryResults run(Query query);
@BetaApi
default QueryResults run(Query query, ExplainOptions explainOptions) {
throw new UnsupportedOperationException("Not implemented.");
}
/**
* Datastore add operation. This method will also allocate id for any entity with an incomplete
* key. As opposed to {@link #add(FullEntity)} and {@link #add(FullEntity...)}, this method will
* defer any necessary id allocation to commit time.
*
* Example of adding multiple entities with deferred id allocation.
*
*
{@code
* IncompleteKey key1 = datastore.newKeyFactory().setKind("MyKind").newKey();
* FullEntity.Builder entityBuilder1 = FullEntity.newBuilder(key1);
* entityBuilder1.set("propertyName", "value1");
* FullEntity entity1 = entityBuilder1.build();
*
* IncompleteKey key2 = datastore.newKeyFactory().setKind("MyKind").newKey();
* FullEntity.Builder entityBuilder2 = FullEntity.newBuilder(key2);
* entityBuilder2.set("propertyName", "value2");
* FullEntity entity2 = entityBuilder2.build();
*
* transaction.addWithDeferredIdAllocation(entity1, entity2);
* Response response = transaction.commit();
* }
*
* @throws DatastoreException if a given entity with a complete key was already added to this
* transaction or if the transaction is no longer active
*/
void addWithDeferredIdAllocation(FullEntity>... entities);
/**
* {@inheritDoc}
*
* Example of adding a single entity.
*
*
{@code
* String keyName = "my_key_name";
* Key key = datastore.newKeyFactory().setKind("MyKind").newKey(keyName);
* Entity.Builder entityBuilder = Entity.newBuilder(key);
* entityBuilder.set("propertyName", "value");
* Entity entity = entityBuilder.build();
* transaction.add(entity);
* transaction.commit();
* }
*
* @throws DatastoreException if a given entity with the same complete key was already added to
* this writer, if the transaction is no longer active or if id allocation for an entity with
* an incomplete key failed
*/
@Override
Entity add(FullEntity> entity);
/**
* {@inheritDoc}
*
* Example of adding multiple entities.
*
*
{@code
* String keyName1 = "my_key_name1";
* String keyName2 = "my_key_name2";
* Key key1 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName1);
* Entity.Builder entityBuilder1 = Entity.newBuilder(key1);
* entityBuilder1.set("propertyName", "value1");
* Entity entity1 = entityBuilder1.build();
*
* Key key2 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName2);
* Entity.Builder entityBuilder2 = Entity.newBuilder(key2);
* entityBuilder2.set("propertyName", "value2");
* Entity entity2 = entityBuilder2.build();
*
* transaction.add(entity1, entity2);
* transaction.commit();
* }
*
* @throws DatastoreException if a given entity with the same complete key was already added to
* this writer, if the transaction is no longer active or if id allocation for an entity with
* an incomplete key failed
*/
@Override
List add(FullEntity>... entities);
/**
* {@inheritDoc} This operation will be converted to {@link #put} operation for entities that were
* already added or put in this writer.
*
* Example of updating multiple entities.
*
*
{@code
* String keyName1 = "my_key_name1";
* String keyName2 = "my_key_name2";
* Key key1 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName1);
* Entity.Builder entityBuilder1 = Entity.newBuilder(key1);
* entityBuilder1.set("propertyName", "value3");
* Entity entity1 = entityBuilder1.build();
*
* Key key2 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName2);
* Entity.Builder entityBuilder2 = Entity.newBuilder(key2);
* entityBuilder2.set("propertyName", "value4");
* Entity entity2 = entityBuilder2.build();
*
* transaction.update(entity1, entity2);
* transaction.commit();
* }
*
* @throws DatastoreException if an entity is marked for deletion in this transaction or if the
* transaction is no longer active
*/
@Override
void update(Entity... entities);
/**
* {@inheritDoc} This operation will also remove from this transaction any prior writes for
* entities with the same keys.
*
* Example of deleting multiple entities.
*
*
{@code
* String keyName1 = "my_key_name1";
* String keyName2 = "my_key_name2";
* Key key1 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName1);
* Key key2 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName2);
* transaction.delete(key1, key2);
* transaction.commit();
* }
*
* @throws DatastoreException upon failure or if no longer active
*/
@Override
void delete(Key... keys);
/**
* Datastore put operation. This method will also allocate id for any entity with an incomplete
* key. As opposed to {@link #put(FullEntity)} and {@link #put(FullEntity...)}, this method will
* defer any necessary id allocation to commit time.
*
* Example of putting multiple entities with deferred id allocation.
*
*
{@code
* IncompleteKey key1 = datastore.newKeyFactory().setKind("MyKind").newKey();
* FullEntity.Builder entityBuilder1 = FullEntity.newBuilder(key1);
* entityBuilder1.set("propertyName", "value1");
* FullEntity entity1 = entityBuilder1.build();
*
* IncompleteKey key2 = datastore.newKeyFactory().setKind("MyKind").newKey();
* FullEntity.Builder entityBuilder2 = FullEntity.newBuilder(key2);
* entityBuilder2.set("propertyName", "value2");
* FullEntity entity2 = entityBuilder2.build();
*
* transaction.putWithDeferredIdAllocation(entity1, entity2);
* Response response = transaction.commit();
* }
*
* @throws IllegalArgumentException if any of the given entities is missing a key
* @throws DatastoreException if no longer active
*/
void putWithDeferredIdAllocation(FullEntity>... entities);
/**
* {@inheritDoc} This operation will also remove from this transaction any prior writes for the
* same entity.
*
* Example of putting a single entity.
*
*
{@code
* String keyName = "my_key_name";
* Key key = datastore.newKeyFactory().setKind("MyKind").newKey(keyName);
* Entity.Builder entityBuilder = Entity.newBuilder(key);
* entityBuilder.set("propertyName", "value");
* Entity entity = entityBuilder.build();
* transaction.put(entity);
* transaction.commit();
* }
*
* @throws DatastoreException if id allocation for an entity with an incomplete key failed or if
* the transaction is no longer active
*/
@Override
Entity put(FullEntity> entity);
/**
* {@inheritDoc} This operation will also remove from this transaction any prior writes for the
* same entities.
*
* Example of putting multiple entities.
*
*
{@code
* String keyName1 = "my_key_name1";
* String keyName2 = "my_key_name2";
* Key key1 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName1);
* Entity.Builder entityBuilder1 = Entity.newBuilder(key1);
* entityBuilder1.set("propertyName", "value1");
* Entity entity1 = entityBuilder1.build();
*
* Key key2 = datastore.newKeyFactory().setKind("MyKind").newKey(keyName2);
* Entity.Builder entityBuilder2 = Entity.newBuilder(key2);
* entityBuilder2.set("propertyName", "value2");
* Entity entity2 = entityBuilder2.build();
*
* transaction.put(entity1, entity2);
* transaction.commit();
* }
*
* @throws DatastoreException if id allocation for an entity with an incomplete key failed or if
* the transaction is no longer active
*/
@Override
List put(FullEntity>... entities);
/**
* Commit the transaction.
*
* Example of committing a transaction.
*
*
{@code
* // create an entity
* KeyFactory keyFactory = datastore.newKeyFactory().setKind("MyKind");
* Key key = datastore.allocateId(keyFactory.newKey());
* Entity entity = Entity.newBuilder(key).set("description", "commit()").build();
*
* // add the entity and commit
* try {
* transaction.put(entity);
* transaction.commit();
* } catch (DatastoreException ex) {
* // handle exception
* }
* }
*
* @throws DatastoreException if could not commit the transaction or if no longer active
*/
Response commit();
/**
* Rollback the transaction.
*
* Example of rolling back a transaction.
*
*
{@code
* // create an entity
* KeyFactory keyFactory = datastore.newKeyFactory().setKind("MyKind");
* Key key = datastore.allocateId(keyFactory.newKey());
* Entity entity = Entity.newBuilder(key).set("description", "rollback()").build();
*
* // add the entity and rollback
* transaction.put(entity);
* transaction.rollback();
* // calling transaction.commit() now would fail
* }
*
* @throws DatastoreException if transaction was already committed
*/
void rollback();
/**
* Returns {@code true} if the transaction is still active (was not committed or rolledback).
*
* Example of verifying if a transaction is active.
*
*
{@code
* // create an entity
* KeyFactory keyFactory = datastore.newKeyFactory().setKind("MyKind");
* Key key = datastore.allocateId(keyFactory.newKey());
* Entity entity = Entity.newBuilder(key).set("description", "active()").build();
* // calling transaction.active() now would return true
* try {
* // add the entity and commit
* transaction.put(entity);
* transaction.commit();
* } finally {
* // if committing succeeded
* // then transaction.active() will be false
* if (transaction.isActive()) {
* // otherwise it's true and we need to rollback
* transaction.rollback();
* }
* }
* }
*/
@Override
boolean isActive();
/** Returns the transaction associated {@link Datastore}. */
Datastore getDatastore();
ByteString getTransactionId();
}