All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.dataspray.singletable.builder.UpdateBuilder Maven / Gradle / Ivy

Go to download

DynamoDB best practices encourages a single-table design that allows multiple record types to reside within the same table. The goal of this library is to improve the experience of Java developers and make it safer to define non-conflicting schema of each record, serializing and deserializing automatically and working with secondary indexes.

The newest version!
package io.dataspray.singletable.builder;

import com.google.common.collect.ImmutableSet;
import io.dataspray.singletable.ExpressionBuilder;
import io.dataspray.singletable.Schema;
import lombok.extern.slf4j.Slf4j;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.ReturnValue;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse;

import java.util.Map;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkNotNull;

@Slf4j
public class UpdateBuilder extends ExpressionBuilder, UpdateItemRequest.Builder> implements ConditionExpressionBuilder>, UpdateExpressionBuilder> {

    public UpdateBuilder(Schema schema) {
        super(schema);
    }

    protected UpdateBuilder getParent() {
        return this;
    }

    private Optional> keyOpt = Optional.empty();
    private boolean upsertCalled = false;
    private boolean conditionExistsCalled = false;
    private boolean suppressNonExistentValidation = false;

    public UpdateBuilder key(Map primaryKey) {
        this.keyOpt = Optional.of(schema.primaryKey(primaryKey));
        return getParent();
    }

    @Override
    public UpdateBuilder upsert(T item, ImmutableSet skipFieldNames) {
        this.upsertCalled = true;
        this.keyOpt = Optional.of(schema.primaryKey(item));
        return super.upsert(item, skipFieldNames);
    }


    public UpdateItemRequest.Builder builder() {
        Expression expression = buildExpression();
        UpdateItemRequest.Builder builder = UpdateItemRequest.builder();
        builder.tableName(schema.tableName());
        expression.updateExpression().ifPresent(builder::updateExpression);
        expression.conditionExpression().ifPresent(builder::conditionExpression);
        expression.expressionAttributeNames().ifPresent(builder::expressionAttributeNames);
        expression.expressionAttributeValues().ifPresent(builder::expressionAttributeValues);
        keyOpt.ifPresent(builder::key);
        expression.builderAdjustments().forEach(c -> c.accept(builder));
        return builder;
    }

    public UpdateItemRequest build() {
        return builder().build();
    }

    public UpdateItemResponse execute(DynamoDbClient dynamo) {
        validateNonExistent();
        return dynamo.updateItem(build());
    }

    public T executeGetUpdated(DynamoDbClient dynamo) {
        validateNonExistent();
        return checkNotNull(schema.fromAttrMap(dynamo.updateItem(builder()
                        .returnValues(ReturnValue.ALL_NEW)
                        .build()).attributes()),
                // This is a weird edge case, this can happen only if:
                // - You didn't have conditionExists() and the item didn't already exist
                // - You didn't actually update any fields in this update
                "No updated item returned");
    }

    public Optional executeGetPrevious(DynamoDbClient dynamo) {
        validateNonExistent();
        return Optional.ofNullable(schema.fromAttrMap(dynamo.updateItem(builder()
                .returnValues(ReturnValue.ALL_OLD)
                .build()).attributes()));
    }

    @Override
    public UpdateBuilder conditionExists() {
        this.conditionExistsCalled = true;
        return super.conditionExists();
    }

    /**
     * Suppresses warning to check whether an item is being updated without a check that it exists.
     */
    public UpdateBuilder suppressNonExistent() {
        this.suppressNonExistentValidation = true;
        return getParent();
    }

    void validateNonExistent() {
        if (!suppressNonExistentValidation && !conditionExistsCalled && !upsertCalled) {
            log.warn("It appears you are updating an item without calling conditionExists() or upsert()"
                    + " which may result in a corrupted entry missing required fields."
                    + " Suppress this warning with suppressNonExistent().");
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy