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

com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel Maven / Gradle / Ivy

Go to download

The AWS SDK for Java with support for OSGi. The AWS SDK for Java provides Java APIs for building software on AWS' cost-effective, scalable, and reliable infrastructure products. The AWS Java SDK allows developers to code against APIs for all of Amazon's infrastructure web services (Amazon S3, Amazon EC2, Amazon SQS, Amazon Relational Database Service, Amazon AutoScaling, etc).

There is a newer version: 1.11.60
Show newest version
/*
 * Copyright 2016-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * 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://aws.amazon.com/apache2.0
 *
 * This file 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.amazonaws.services.dynamodbv2.datamodeling;

import static com.amazonaws.services.dynamodbv2.model.KeyType.HASH;
import static com.amazonaws.services.dynamodbv2.model.KeyType.RANGE;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.SaveBehavior;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.Id;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.LocalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProjectionType;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;

/**
 * Table model.
 *
 * @param  The object type.
 */
public final class DynamoDBMapperTableModel implements DynamoDBTypeConverter,T>  {

    private final Map> fields;
    private final Map> keys;
    private final Map gsis;
    private final Map lsis;
    private final Properties properties;

    /**
     * Constructs a new table model for the specified class.
     * @param builder The builder.
     */
    private DynamoDBMapperTableModel(final DynamoDBMapperTableModel.Builder builder) {
        this.properties = builder.properties;
        this.fields = builder.fields;
        this.keys = builder.keys;
        this.gsis = builder.globalSecondaryIndexes();
        this.lsis = builder.localSecondaryIndexes();
    }

    /**
     * Gets the object type.
     * @return The object type.
     */
    public Class targetType() {
        return properties.targetType();
    }

    /**
     * Gets the table name.
     * @return The table name.
     */
    public String tableName() {
        return properties.tableName();
    }

    /**
     * Gets all the field models for the given class.
     * @return The field models.
     */
    public Collection> fields() {
        return fields.values();
    }

    /**
     * Gets the field model for a given attribute.
     * @param  The field model's value type.
     * @param attributeName The attribute name.
     * @return The field model.
     */
    public  DynamoDBMapperFieldModel field(final String attributeName) {
        if (!fields.containsKey(attributeName)) {
            throw new DynamoDBMappingException((new Id(targetType(), attributeName)).format("not mapped on object model"));
        }
        return (DynamoDBMapperFieldModel)fields.get(attributeName);
    }

    /**
     * Gets the field model for a given key type.
     * @param  The field model's value type.
     * @param keyType The key type.
     * @return The field model.
     */
    public  DynamoDBMapperFieldModel field(final KeyType keyType) {
        if (!keys.containsKey(keyType)) {
            throw new DynamoDBMappingException(new Id(targetType(), keyType.name()).format("not mapped on object model"));
        }
        return (DynamoDBMapperFieldModel)keys.get(keyType);
    }

    /**
     * Gets all the key field models for the given class.
     * @return The field models.
     */
    public Collection> keys() {
        return keys.values();
    }

    /**
     * Gets the hash key field model for the specified type.
     * @param  The hash key type.
     * @return The hash key field model.
     * @throws DynamoDBMappingException If the hash key is not present.
     */
    public  DynamoDBMapperFieldModel hashKey() {
        return (DynamoDBMapperFieldModel)field(HASH);
    }

    /**
     * Gets the range key field model for the specified type.
     * @param  The range key type.
     * @return The range key field model.
     * @throws DynamoDBMappingException If the range key is not present.
     */
    public  DynamoDBMapperFieldModel rangeKey() {
        return (DynamoDBMapperFieldModel)field(RANGE);
    }

    /**
     * Gets the range key field model for the specified type.
     * @param  The range key type.
     * @return The range key field model, or null if not present.
     */
    public  DynamoDBMapperFieldModel rangeKeyIfExists() {
        return (DynamoDBMapperFieldModel)keys.get(RANGE);
    }

    /**
     * Gets the global secondary indexes for the given class.
     * @return The map of index name to GlobalSecondaryIndexes.
     */
    public Collection globalSecondaryIndexes() {
        if (gsis.isEmpty()) {
            return Collections.emptyList();
        }
        final Collection copies = new ArrayList(gsis.size());
        for (final String indexName : gsis.keySet()) {
            copies.add(globalSecondaryIndex(indexName));
        }
        return copies;
    }

    /**
     * Gets the global secondary index.
     * @param indexName The index name.
     * @return The global secondary index or null.
     */
    public GlobalSecondaryIndex globalSecondaryIndex(final String indexName) {
        if (!gsis.containsKey(indexName)) {
            return null;
        }
        final GlobalSecondaryIndex gsi = gsis.get(indexName);
        final GlobalSecondaryIndex copy = new GlobalSecondaryIndex().withIndexName(gsi.getIndexName());
        copy.withProjection(new Projection().withProjectionType(gsi.getProjection().getProjectionType()));
        for (final KeySchemaElement key : gsi.getKeySchema()) {
            copy.withKeySchema(new KeySchemaElement(key.getAttributeName(), key.getKeyType()));
        }
        return copy;
    }

    /**
     * Gets the local secondary indexes for the given class.
     * @param indexNames The index names.
     * @return The map of index name to LocalSecondaryIndexes.
     */
    public Collection localSecondaryIndexes() {
        if (lsis.isEmpty()) {
            return Collections.emptyList();
        }
        final Collection copies = new ArrayList(lsis.size());
        for (final String indexName : lsis.keySet()) {
            copies.add(localSecondaryIndex(indexName));
        }
        return copies;
    }

    /**
     * Gets the local secondary index by name.
     * @param indexNames The index name.
     * @return The local secondary index, or null.
     */
    public LocalSecondaryIndex localSecondaryIndex(final String indexName) {
        if (!lsis.containsKey(indexName)) {
            return null;
        }
        final LocalSecondaryIndex lsi = lsis.get(indexName);
        final LocalSecondaryIndex copy = new LocalSecondaryIndex().withIndexName(lsi.getIndexName());
        copy.withProjection(new Projection().withProjectionType(lsi.getProjection().getProjectionType()));
        for (final KeySchemaElement key : lsi.getKeySchema()) {
            copy.withKeySchema(new KeySchemaElement(key.getAttributeName(), key.getKeyType()));
        }
        return copy;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Map convert(final T object) {
        if (object == null) {
            return null;
        }
        final Map map = new LinkedHashMap();
        for (final DynamoDBMapperFieldModel field : fields()) {
            final AttributeValue value = field.getAndConvert(object);
            if (value != null) {
                map.put(field.name(), value);
            }
        }
        return map;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T unconvert(final Map object) {
        final T result;
        try {
            result = targetType().newInstance();
        } catch (final Exception e) {
            throw new DynamoDBMappingException("could not instantiate " + targetType(), e);
        }
        if (object != null && !object.isEmpty()) {
            for (final DynamoDBMapperFieldModel field : fields()) {
                final AttributeValue value = object.get(field.name());
                if (value != null) {
                    field.unconvertAndSet(result, value);
                }
            }
        }
        return result;
    }

    /**
     * Creates a new object instance with the keys populated.
     * @param  The hash key type.
     * @param  The range key type.
     * @param hashKey The hash key.
     * @param rangeKey The range key (optional if not present on table).
     * @return The new instance.
     */
    public  T newKey(final H hashKey, final R rangeKey) {
        final T key;
        try {
            key = targetType().newInstance();
        } catch (final Exception e) {
            throw new DynamoDBMappingException("could not instantiate " + targetType(), e);
        }
        if (hashKey != null) {
            final DynamoDBMapperFieldModel hk = hashKey();
            hk.set(key, hashKey);
        }
        if (rangeKey != null) {
            final DynamoDBMapperFieldModel rk = rangeKey();
            rk.set(key, rangeKey);
        }
        return key;
    }

    /**
     * Creates a new key map from the specified hash and range key.
     * @param  The hash key type.
     * @param  The range key type.
     * @param hashKey The hash key.
     * @param rangeKey The range key (optional if not present on table).
     * @return The key map.
     */
    public  Map mapKey(final H hashKey, final R rangeKey) {
        final Map key = new LinkedHashMap(4);
        if (hashKey != null) {
            final DynamoDBMapperFieldModel hk = hashKey();
            key.put(hk.name(), hk.convert(hashKey));
        }
        if (rangeKey != null) {
            final DynamoDBMapperFieldModel rk = rangeKey();
            key.put(rk.name(), rk.convert(rangeKey));
        }
        if (key.isEmpty()) {
            throw new DynamoDBMappingException("no key(s) present on " + targetType());
        }
        return key;
    }

    /**
     * Creates a new key map from the specified object.
     * @param  The hash key type.
     * @param  The range key type.
     * @param object The object instance.
     * @return The key map.
     */
    public  Map mapKey(final T object) {
        final Map key = new LinkedHashMap(4);
        for (final DynamoDBMapperFieldModel field : keys()) {
            final AttributeValue value = field.getAndConvert(object);
            if (value == null) {
                throw new DynamoDBMappingException(field.id().format("must not have null value for %s key", field.keyType()));
            }
            key.put(field.name(), value);
        }
        if (key.isEmpty()) {
            throw new DynamoDBMappingException("no key(s) present on " + targetType());
        }
        return key;
    }

    /**
     * Determnes if any of the primary keys require auto-generation.
     * @param object The object instance.
     * @param saveBehavior The save behaviour.
     * @return True if any keys should be auto-generated.
     */
    public boolean anyKeyGeneratable(final T object, final SaveBehavior saveBehavior) {
        if (keys().isEmpty()) {
            throw new DynamoDBMappingException("no key(s) present on " + targetType());
        }
        for (final DynamoDBMapperFieldModel field : keys()) {
            if (field.canGenerate(object, saveBehavior, null)) {
                return true;
            }
        }
        return false;
    }

    /**
     * The table model properties.
     */
    static interface Properties {
        public Class targetType();
        public String tableName();

         /**
          * Properties builder.
          */
        static final class Builder implements Properties {
            private Class targetType;
            private String tableName;

            /**
             * Populates the builder properties with the specified defaults.
             * @param defaults The default properties.
             * @return This builder instance for chaining.
             */
            public Builder with(final Properties defaults) {
                this.targetType = defaults.targetType();
                this.tableName = defaults.tableName();
                return this;
            }

            /**
             * Sets the target type.
             * @param targetType The target type.
             * @return This builder instance for chaining.
             */
            public Builder withTargetType(final Class targetType) {
                this.targetType = targetType;
                return this;
            }

            /**
             * Sets the table name.
             * @param tableName The table name.
             * @return This builder instance for chaining.
             */
            public Builder withTableName(final String tableName) {
                this.tableName = tableName;
                return this;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public Class targetType() {
                return targetType;
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public String tableName() {
                return tableName;
            }
        }
    }

    /**
     * {@link DynamoDBMapperTableModel} builder.
     */
    static class Builder {
        private final Map> fields;
        private final Map> keys;
        private final Properties.Builder properties;

        /**
         * Construts a new builder.
         * @param defaults The default properties.
         */
        public Builder(final Properties defaults) {
            this.fields = new LinkedHashMap>();
            this.keys = new LinkedHashMap>(4);
            this.properties = new Properties.Builder().with(defaults);
        }

        /**
         * Sets the table name.
         * @param tableName The table name.
         * @return This builder instance for chaining.
         */
        public Builder withTableName(final String tableName) {
            this.properties.withTableName(tableName);
            return this;
        }

        /**
         * Adds a field model to this builder.
         * @param field The field model.
         * @return This builder instance for chaining.
         */
        public Builder with(final DynamoDBMapperFieldModel field) {
            if (fields.put(field.name(), field) != null) {
                throw new DynamoDBMappingException(field.id().format("must not duplicate attribute name"));
            }
            if (field.keyType() != null && keys.put(field.keyType(), field) != null) {
                throw new DynamoDBMappingException(field.id().format("must not specify multiple %s key(s)", field.keyType()));
            }
            return this;
        }

        /**
         * Builds the GSI mappings.
         * @return The mappings.
         */
        public Map globalSecondaryIndexes() {
            final Map map = new LinkedHashMap();
            for (final DynamoDBMapperFieldModel field : fields.values()) {
                for (final String indexName : field.globalSecondaryIndexNames(HASH)) {
                    final GlobalSecondaryIndex gsi = new GlobalSecondaryIndex().withIndexName(indexName);
                    if (map.put(indexName, gsi) != null) {
                        throw new DynamoDBMappingException(field.id().format("must not contain duplicate GSI named %s", indexName));
                    }
                    gsi.withProjection(new Projection().withProjectionType(ProjectionType.KEYS_ONLY));
                    gsi.withKeySchema(new KeySchemaElement(field.name(), HASH));
                }
            }
            for (final DynamoDBMapperFieldModel field : fields.values()) {
                for (final String indexName : field.globalSecondaryIndexNames(RANGE)) {
                    final GlobalSecondaryIndex gsi = map.get(indexName);
                    if (gsi == null) {
                        throw new DynamoDBMappingException(field.id().format("no HASH key present for GSI named %s", indexName));
                    }
                    gsi.withKeySchema(new KeySchemaElement(field.name(), RANGE));
                }
            }
            return Collections.unmodifiableMap(map);
        }

        /**
         * Builds the LSI mappings.
         * @return The mappings.
         */
        public Map localSecondaryIndexes() {
            final Map map = new LinkedHashMap();
            for (final DynamoDBMapperFieldModel field : fields.values()) {
                for (final String indexName : field.localSecondaryIndexNames()) {
                    final LocalSecondaryIndex lsi = new LocalSecondaryIndex().withIndexName(indexName);
                    if (map.put(indexName, lsi) != null) {
                        throw new DynamoDBMappingException(field.id().format("must not contain duplicate LocalSecondaryIndexes named %s", indexName));
                    }
                    lsi.withProjection(new Projection().withProjectionType(ProjectionType.KEYS_ONLY));
                    lsi.withKeySchema(new KeySchemaElement(keys.get(HASH).name(), HASH));
                    lsi.withKeySchema(new KeySchemaElement(field.name(), RANGE));
                }
            }
            return Collections.unmodifiableMap(map);
        }

        /**
         * Builds the instance.
         * @return The built instance.
         */
        public final DynamoDBMapperTableModel build() {
            return new DynamoDBMapperTableModel(this);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy