Skip to content

Commit

Permalink
feat: add support for insert and insertAll in DatastoreOperations (#1729
Browse files Browse the repository at this point in the history
)
  • Loading branch information
cfranzen committed Apr 21, 2023
1 parent 7f34f6d commit a478264
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 132 deletions.
1 change: 1 addition & 0 deletions docs/src/main/asciidoc/datastore.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,7 @@ this.datastoreTemplate.save(t);
----

The `save` method behaves as update-or-insert.
In contrast, the `insert` method will fail if an entity already exists.

===== Partial Update

Expand Down
3 changes: 2 additions & 1 deletion docs/src/main/md/datastore.md
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,8 @@ Trader t = new Trader();
this.datastoreTemplate.save(t);
```

The `save` method behaves as update-or-insert.
The `save` method behaves as update-or-insert.
In contrast, the `insert` method will fail if an entity already exists.

##### Partial Update

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.cloud.spring.data.datastore.core;

import com.google.cloud.datastore.BaseEntity;
import com.google.cloud.datastore.DatastoreException;
import com.google.cloud.datastore.Key;
import com.google.cloud.datastore.KeyQuery;
import com.google.cloud.datastore.Query;
Expand Down Expand Up @@ -71,6 +72,30 @@ public interface DatastoreOperations {
*/
<T> Iterable<T> saveAll(Iterable<T> entities, Key... ancestors);

/**
* Inserts an instance of an object to Cloud Datastore. Throws a DatastoreException if an entry with same ID
* already exists. Ancestors can be added only to entries with Key ids.
*
* @param instance the instance to save.
* @param ancestors ancestors that should be added to the entry
* @param <T> the type of the object to save
* @throws DatastoreException If the entity already exists
* @return the instance that was saved.
*/
<T> T insert(T instance, Key... ancestors);

/**
* Saves multiple instances of objects to Cloud Datastore. Throws a DatastoreException if any entry with one of
* the IDs already exists. Ancestors can be added only to entries with Key ids.
*
* @param entities the objects to save.
* @param ancestors ancestors that should be added to each entry
* @param <T> the type of entities to save
* @throws DatastoreException If any entity already exists
* @return the entities that were saved.
*/
<T> Iterable<T> insertAll(Iterable<T> entities, Key... ancestors);

/**
* Delete an entity from Cloud Datastore. Deleting IDs that do not exist in Cloud Datastore will
* result in no operation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -144,21 +145,44 @@ public <T> T findById(Object id, Class<T> entityClass) {
@Override
public <T> T save(T instance, Key... ancestors) {
List<T> instances = Collections.singletonList(instance);
saveEntities(instances, ancestors);
insertOrSaveEntities(instances, ancestors, getDatastoreReadWriter()::put);
return instance;
}

@Override
public <T> Iterable<T> saveAll(Iterable<T> entities, Key... ancestors) {
insertOrSaveEntities(entities, ancestors, getDatastoreReadWriter()::put);
return entities;
}

@Override
public <T> T insert(final T instance, final Key... ancestors) {
List<T> instances = Collections.singletonList(instance);
insertOrSaveEntities(instances, ancestors, getDatastoreReadWriter()::add);
return instance;
}

@Override
public <T> Iterable<T> insertAll(final Iterable<T> entities, final Key... ancestors) {
insertOrSaveEntities(entities, ancestors, getDatastoreReadWriter()::add);
return entities;
}

private <T> void insertOrSaveEntities(Iterable<T> iterable, Key[] ancestors, Consumer<FullEntity<?>[]> consumer) {
List<T> instances;
if (entities instanceof List) {
instances = (List<T>) entities;
if (iterable instanceof List) {
instances = (List<T>) iterable;
} else {
instances = new ArrayList<>();
entities.forEach(instances::add);
iterable.forEach(instances::add);
}

if (!instances.isEmpty()) {
maybeEmitEvent(new BeforeSaveEvent(instances));
List<Entity> entities = getEntitiesForSave(instances, new HashSet<>(), ancestors);
SliceUtil.sliceAndExecute(entities.toArray(new Entity[0]), this.maxWriteSize, consumer);
maybeEmitEvent(new AfterSaveEvent(entities, instances));
}
saveEntities(instances, ancestors);
return entities;
}

private <T> List<Entity> getEntitiesForSave(
Expand All @@ -174,16 +198,6 @@ private <T> List<Entity> getEntitiesForSave(
return entitiesForSave;
}

private <T> void saveEntities(List<T> instances, Key[] ancestors) {
if (!instances.isEmpty()) {
maybeEmitEvent(new BeforeSaveEvent(instances));
List<Entity> entities = getEntitiesForSave(instances, new HashSet<>(), ancestors);
SliceUtil.sliceAndExecute(
entities.toArray(new Entity[0]), this.maxWriteSize, getDatastoreReadWriter()::put);
maybeEmitEvent(new AfterSaveEvent(entities, instances));
}
}

@Override
public <T> void deleteById(Object id, Class<T> entityClass) {
performDelete(
Expand Down
Loading

0 comments on commit a478264

Please sign in to comment.