All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel Maven / Gradle / Ivy
/*
* Copyright 2016-2021 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 static com.amazonaws.services.dynamodbv2.model.ProjectionType.KEYS_ONLY;
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.EnumMap;
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 globalSecondaryIndexes;
private final Map localSecondaryIndexes;
private final Map> versions;
private final Map> fields;
private final Map> keys;
private final DynamoDBMapperTableModel.Properties properties;
private final Class targetType;
/**
* Constructs a new table model for the specified class.
* @param builder The builder.
*/
private DynamoDBMapperTableModel(final DynamoDBMapperTableModel.Builder builder) {
this.globalSecondaryIndexes = builder.globalSecondaryIndexes();
this.localSecondaryIndexes = builder.localSecondaryIndexes();
this.versions = builder.versions();
this.fields = builder.fields();
this.keys = builder.keys();
this.properties = builder.properties;
this.targetType = builder.targetType;
}
/**
* Gets the object type.
* @return The object type.
*/
public Class targetType() {
return this.targetType;
}
/**
* 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.
*/
@SuppressWarnings("unchecked")
public DynamoDBMapperFieldModel field(final String attributeName) {
final DynamoDBMapperFieldModel field = (DynamoDBMapperFieldModel)fields.get(attributeName);
if (field == null) {
throw new DynamoDBMappingException(
targetType.getSimpleName() + "[" + attributeName + "]; no mapping for attribute by name"
);
}
return field;
}
/**
* 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.
*/
@SuppressWarnings("unchecked")
public DynamoDBMapperFieldModel hashKey() {
final DynamoDBMapperFieldModel field = (DynamoDBMapperFieldModel)keys.get(HASH);
if (field == null) {
throw new DynamoDBMappingException(
targetType.getSimpleName() + "; no mapping for HASH key"
);
}
return field;
}
/**
* 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.
*/
@SuppressWarnings("unchecked")
public DynamoDBMapperFieldModel rangeKey() {
final DynamoDBMapperFieldModel field = (DynamoDBMapperFieldModel)keys.get(RANGE);
if (field == null) {
throw new DynamoDBMappingException(
targetType.getSimpleName() + "; no mapping for RANGE key"
);
}
return field;
}
/**
* 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.
*/
@SuppressWarnings("unchecked")
public DynamoDBMapperFieldModel rangeKeyIfExists() {
return (DynamoDBMapperFieldModel)keys.get(RANGE);
}
/**
* Gets all the version fields for the given class.
* @return The field models.
*/
public Collection> versions() {
return versions.values();
}
/**
* Indicates if this table has any versioned attributes.
* @return True if any versioned attributes, false otherwise.
*/
public boolean versioned() {
return !versions.isEmpty();
}
/**
* Gets the global secondary indexes for the given class.
* @return The map of index name to GlobalSecondaryIndexes.
*/
public Collection globalSecondaryIndexes() {
if (globalSecondaryIndexes.isEmpty()) {
return null;
}
final Collection copies = new ArrayList(globalSecondaryIndexes.size());
for (final String indexName : globalSecondaryIndexes.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 (!globalSecondaryIndexes.containsKey(indexName)) {
return null;
}
final GlobalSecondaryIndex gsi = globalSecondaryIndexes.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 (localSecondaryIndexes.isEmpty()) {
return null;
}
final Collection copies = new ArrayList(localSecondaryIndexes.size());
for (final String indexName : localSecondaryIndexes.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 (!localSecondaryIndexes.containsKey(indexName)) {
return null;
}
final LocalSecondaryIndex lsi = localSecondaryIndexes.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) {
final Map map = new LinkedHashMap();
for (final DynamoDBMapperFieldModel field : fields()) {
try {
final AttributeValue value = field.getAndConvert(object);
if (value != null) {
map.put(field.name(), value);
}
} catch (final RuntimeException e) {
throw new DynamoDBMappingException(
targetType.getSimpleName() + "[" + field.name() + "]; could not convert attribute", e
);
}
}
return map;
}
/**
* {@inheritDoc}
*/
@Override
public T unconvert(final Map object) {
final T result = StandardBeanProperties.DeclaringReflect.newInstance(targetType);
if (!object.isEmpty()) {
for (final DynamoDBMapperFieldModel field : fields()) {
try {
final AttributeValue value = object.get(field.name());
if (value != null) {
field.unconvertAndSet(result, value);
}
} catch (final RuntimeException e) {
throw new DynamoDBMappingException(
targetType.getSimpleName() + "[" + field.name() + "]; could not unconvert attribute", e
);
}
}
}
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 createKey(final H hashKey, final R rangeKey) {
final T key = StandardBeanProperties.DeclaringReflect.newInstance(targetType);
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 object.
* @param The hash key type.
* @param The range key type.
* @param object The object instance.
* @return The key map.
*/
public Map convertKey(final T key) {
final DynamoDBMapperFieldModel hk = this.hashKey();
final DynamoDBMapperFieldModel rk = this.rangeKeyIfExists();
return this.convertKey(hk.get(key), (rk == null ? (R)null : rk.get(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 convertKey(final H hashKey, final R rangeKey) {
final Map key = new LinkedHashMap(4);
final DynamoDBMapperFieldModel hk = this.hashKey();
final AttributeValue hkValue = hashKey == null ? null : hk.convert(hashKey);
if (hkValue != null) {
key.put(hk.name(), hkValue);
} else {
throw new DynamoDBMappingException(
targetType.getSimpleName() + "[" + hk.name() + "]; no HASH key value present"
);
}
final DynamoDBMapperFieldModel rk = this.rangeKeyIfExists();
final AttributeValue rkValue = rangeKey == null ? null : rk.convert(rangeKey);
if (rkValue != null) {
key.put(rk.name(), rkValue);
} else if (rk != null) {
throw new DynamoDBMappingException(
targetType.getSimpleName() + "[" + rk.name() + "]; no RANGE key value present"
);
}
return key;
}
/**
* {@link DynamoDBMapperTableModel} builder.
*/
static class Builder {
private final Map> versions;
private final Map> fields;
private final Map> keys;
private final Properties properties;
private final Class targetType;
public Builder(Class targetType, Properties properties) {
this.versions = new LinkedHashMap>(4);
this.fields = new LinkedHashMap>();
this.keys = new EnumMap>(KeyType.class);
this.properties = properties;
this.targetType = targetType;
}
public Builder with(final DynamoDBMapperFieldModel field) {
fields.put(field.name(), field);
if (field.keyType() != null) {
keys.put(field.keyType(), field);
}
if (field.versioned()) {
versions.put(field.name(), field);
}
return this;
}
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(
targetType.getSimpleName() + "[" + field.name() + "]; must not duplicate GSI " + indexName
);
}
gsi.withProjection(new Projection().withProjectionType(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(
targetType.getSimpleName() + "[" + field.name() + "]; no HASH key for GSI " + indexName
);
}
gsi.withKeySchema(new KeySchemaElement(field.name(), RANGE));
}
}
if (map.isEmpty()) {
return Collections.emptyMap();
}
return Collections.unmodifiableMap(map);
}
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(
targetType.getSimpleName() + "[" + field.name() + "]; must not duplicate LSI " + indexName
);
}
lsi.withProjection(new Projection().withProjectionType(KEYS_ONLY));
lsi.withKeySchema(new KeySchemaElement(keys.get(HASH).name(), HASH));
lsi.withKeySchema(new KeySchemaElement(field.name(), RANGE));
}
}
if (map.isEmpty()) {
return Collections.emptyMap();
}
return Collections.unmodifiableMap(map);
}
private Map> versions() {
if (versions.isEmpty()) {
return Collections.>emptyMap();
}
return Collections.unmodifiableMap(versions);
}
public Map> fields() {
if (fields.isEmpty()) {
return Collections.>emptyMap();
}
return Collections.unmodifiableMap(fields);
}
public Map> keys() {
if (keys.isEmpty()) {
return Collections.>emptyMap();
}
return Collections.unmodifiableMap(keys);
}
public DynamoDBMapperTableModel build() {
final DynamoDBMapperTableModel result = new DynamoDBMapperTableModel(this);
if (properties.tableName() != null) {
result.hashKey(); //<- make sure the hash key is present
}
return result;
}
}
/**
* The table model properties.
*/
static interface Properties {
public String tableName();
static final class Immutable implements Properties {
private final String tableName;
public Immutable(final Properties properties) {
this.tableName = properties.tableName();
}
@Override
public String tableName() {
return this.tableName;
}
}
}
}