• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * The entry-point for all DynamoDB client creation. All Java Dynamo features will be accessible through this single class. This
3  * enables easy access to the different abstractions that the AWS SDK for Java provides (in exchange for a bigger JAR size).
4  *
5  * <p>
6  *     <b>Maven Module Location</b>
7  *     This would be in a separate maven module (software.amazon.awssdk:dynamodb-all) that depends on all other DynamoDB modules
8  *     (software.amazon.awssdk:dynamodb, software.amazon.awssdk:dynamodb-document). Customers that only want one specific client
9  *     could instead depend directly on the module that contains it.
10  * </p>
11  */
12 @ThreadSafe
13 public interface DynamoDb {
14     /**
15      * Create a low-level DynamoDB client with default configuration. Equivalent to DynamoDbClient.create().
16      * Already GA in module software.amazon.awssdk:dynamodb.
17      */
client()18     DynamoDbClient client();
19 
20     /**
21      * Create a low-level DynamoDB client builder. Equivalent to DynamoDbClient.builder().
22      * Already GA in module software.amazon.awssdk:dynamodb.
23      */
clientBuilder()24     DynamoDbClientBuilder clientBuilder();
25 
26     /**
27      * Create a high-level "document" DynamoDB client with default configuration.
28      *
29      * Usage Example:
30      * <code>
31      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
32      *         client.listTables().tables().forEach(System.out::println);
33      *     }
34      * </code>
35      *
36      * @see DynamoDbDocumentClient
37      */
documentClient()38     DynamoDbDocumentClient documentClient();
39 
40     /**
41      * Create a high-level "document" DynamoDB client builder that can configure and create high-level "document" DynamoDB
42      * clients.
43      *
44      * Usage Example:
45      * <code>
46      *     try (DynamoDbClient lowLevelClient = DynamoDb.client();
47      *          DynamoDbDocumentClient client = DynamoDb.documentClientBuilder()
48      *                                                  .dynamoDbClient(lowLevelClient)
49      *                                                  .build()) {
50      *         client.listTables().tables().forEach(System.out::println);
51      *     }
52      * </code>
53      *
54      * @see DynamoDbDocumentClient.Builder
55      */
documentClientBuilder()56     DynamoDbDocumentClient.Builder documentClientBuilder();
57 }
58 
59 /**
60  * A synchronous client for interacting with DynamoDB. While the low-level {@link DynamoDbClient} is generated from a service
61  * model, this client is hand-written and provides a richer client experience for DynamoDB.
62  *
63  * Features:
64  * <ol>
65  *     <li>Representations of DynamoDB resources, like {@link Table}s and {@link Item}s.</li>
66  *     <li>Support for Java-specific types, like {@link Instant} and {@link BigDecimal}.</li>
67  *     <li>Support for reading and writing custom objects (eg. Java Beans, POJOs).</li>
68  * </ol>
69  *
70  * All {@link DynamoDbDocumentClient}s should be closed via {@link #close()}.
71  */
72 @ThreadSafe
73 public interface DynamoDbDocumentClient extends SdkAutoCloseable {
74     /**
75      * Create a {@link DynamoDbDocumentClient} with default configuration.
76      *
77      * Equivalent statements:
78      * <ol>
79      *     <li>{@code DynamoDb.documentClient()}</li>
80      *     <li>{@code DynamoDb.documentClientBuilder().build()}</li>
81      *     <li>{@code DynamoDbDocumentClient.builder().build()}</li>
82      * </ol>
83      *
84      * Usage Example:
85      * <code>
86      *     try (DynamoDbDocumentClient client = DynamoDbDocumentClient.create()) {
87      *         client.listTables().table().forEach(System.out::println);
88      *     }
89      * </code>
90      */
create()91     static DynamoDbDocumentClient create();
92 
93     /**
94      * Create a {@link DynamoDbDocumentClient.Builder} that can be used to create a {@link DynamoDbDocumentClient} with custom
95      * configuration.
96      *
97      * Equivalent to {@code DynamoDb.documentClientBuilder()}.
98      *
99      * Usage Example:
100      * <code>
101      *     try (DynamoDbClient lowLevelClient = DynamoDbClient.create();
102      *          DynamoDbDocumentClient client = DynamoDbDocumentClient.builder()
103      *                                                                .dynamoDbClient(lowLevelClient)
104      *                                                                .build()) {
105      *         client.listTables().tables().forEach(System.out::println);
106      *     }
107      * </code>
108      */
builder()109     static DynamoDbDocumentClient.Builder builder();
110 
111     /**
112      * Create a Dynamo DB table that does not already exist. If the table exists already, use {@link #getTable(String)}.
113      *
114      * Usage Example:
115      * <code>
116      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
117      *         ProvisionedCapacity tableCapacity = ProvisionedCapacity.builder()
118      *                                                                .readCapacity(5)
119      *                                                                .writeCapacity(5)
120      *                                                                .build();
121      *
122      *         KeySchema tableKeys = KeySchema.builder()
123      *                                        .putKey("partition-key", ItemAttributeIndexType.PARTITION_KEY)
124      *                                        .build();
125      *
126      *         client.createTable(CreateTableRequest.builder()
127      *                                              .tableName("my-table")
128      *                                              .provisionedCapacity(tableCapacity)
129      *                                              .keySchema(tableKeys)
130      *                                              .build());
131      *
132      *         System.out.println("Table created successfully.");
133      *     } catch (TableAlreadyExistsException e) {
134      *         System.out.println("Table creation failed.");
135      *     }
136      * </code>
137      */
createTable(CreateTableRequest createTableRequest)138     CreateTableResponse createTable(CreateTableRequest createTableRequest)
139             throws TableAlreadyExistsException;
140 
141     /**
142      * Get a specific DynamoDB table, based on its table name. If the table does not exist, use {@link #createTable}.
143      *
144      * Usage Example:
145      * <code>
146      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
147      *         Table table = client.getTable("my-table");
148      *         System.out.println(table);
149      *     } catch (NoSuchTableException e) {
150      *         System.out.println("Table does not exist.");
151      *     }
152      * </code>
153      */
getTable(String tableName)154     Table getTable(String tableName)
155             throws NoSuchTableException;
156 
157     /**
158      * Get a lazily-populated iterable over all DynamoDB tables on the current account and region.
159      *
160      * Usage Example:
161      * <code>
162      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
163      *         String tables = client.listTables().tables().stream()
164      *                               .map(Table::name)
165      *                               .collect(Collectors.joining(","));
166      *         System.out.println("Current Tables: " + tables);
167      *     }
168      * </code>
169      */
listTables()170     ListTablesResponse listTables();
171 
172     /**
173      * The builder for the high-level DynamoDB client. This is used by customers to configure the high-level client with default
174      * values to be applied across all client operations.
175      *
176      * This can be created via {@link DynamoDb#documentClientBuilder()} or {@link DynamoDbDocumentClient#builder()}.
177      */
178     interface Builder {
179         /**
180          * Configure the DynamoDB document client with a low-level DynamoDB client.
181          *
182          * Default: {@code DynamoDbClient.create()}
183          */
dynamoDbClient(DynamoDbClient client)184         DynamoDbDocumentClient.Builder dynamoDbClient(DynamoDbClient client);
185 
186         /**
187          * Configure the DynamoDB document client with a specific set of configuration values that override the defaults.
188          *
189          * Default: {@code DocumentClientConfiguration.create()}
190          */
documentClientConfiguration(DocumentClientConfiguration configuration)191         DynamoDbDocumentClient.Builder documentClientConfiguration(DocumentClientConfiguration configuration);
192 
193         /**
194          * Create a DynamoDB document client with all of the configured values.
195          */
build()196         DynamoDbDocumentClient build();
197     }
198 }
199 
200 /**
201  * Configuration for a {@link DynamoDbDocumentClient}. This specific configuration is applied globally across all tables created
202  * by a client builder.
203  *
204  * @see DynamoDbDocumentClient.Builder#documentClientConfiguration(DocumentClientConfiguration)
205  */
206 @ThreadSafe
207 public interface DocumentClientConfiguration {
208     /**
209      * Create document client configuration with default values.
210      */
create()211     static DocumentClientConfiguration create();
212 
213     /**
214      * Create a builder instance, with an intent to override default values.
215      */
builder()216     static DocumentClientConfiguration.Builder builder();
217 
218     interface Builder {
219         /**
220          * Configure the type converters that should be applied globally across all {@link Table}s from the client. This can
221          * also be overridden at the Item level.
222          *
223          * The following type conversions are supported by default:
224          * <ul>
225          *     <li>{@link Number} -> {@link ItemAttributeValueType#NUMBER}</li>
226          *     <li>{@link Temporal} -> {@link ItemAttributeValueType#NUMBER}</li>
227          *     <li>{@link CharSequence} -> {@link ItemAttributeValueType#STRING}</li>
228          *     <li>{@link UUID} -> {@link ItemAttributeValueType#STRING}</li>
229          *     <li>{@link byte[]} -> {@link ItemAttributeValueType#BYTES}</li>
230          *     <li>{@link ByteBuffer} -> {@link ItemAttributeValueType#BYTES}</li>
231          *     <li>{@link BytesWrapper} -> {@link ItemAttributeValueType#BYTES}</li>
232          *     <li>{@link InputStream} -> {@link ItemAttributeValueType#BYTES}</li>
233          *     <li>{@link File} -> {@link ItemAttributeValueType#BYTES}</li>
234          *     <li>{@link Boolean} -> {@link ItemAttributeValueType#BOOLEAN}</li>
235          *     <li>{@link Collection} -> {@link ItemAttributeValueType#LIST_OF_*}</li>
236          *     <li>{@link Stream} -> {@link ItemAttributeValueType#LIST_OF_*}</li>
237          *     <li>{@link Iterable} -> {@link ItemAttributeValueType#LIST_OF_*}</li>
238          *     <li>{@link Iterator} -> {@link ItemAttributeValueType#LIST_OF_*}</li>
239          *     <li>{@link Enumeration} -> {@link ItemAttributeValueType#LIST_OF_*}</li>
240          *     <li>{@link Optional} -> {@link ItemAttributeValue#*}</li>
241          *     <li>{@link Map} -> {@link ItemAttributeValueType#ITEM}</li>
242          *     <li>{@link Object} -> {@link ItemAttributeValueType#ITEM}</li>
243          *     <li>{@link null} -> {@link ItemAttributeValueType#NULL}</li>
244          * </ul>
245          *
246          * Usage Example:
247          * <code>
248          *     DocumentClientConfiguration clientConfiguration =
249          *             DocumentClientConfiguration.builder()
250          *                                        .addConverter(InstantsAsStringsConverter.create())
251          *                                        .build();
252          *
253          *     try (DynamoDbDocumentClient client = DynamoDb.documentClientBuilder()
254          *                                                  .documentClientConfiguration(clientConfiguration)
255          *                                                  .build()) {
256          *
257          *         Table table = client.getTable("my-table");
258          *         UUID id = UUID.randomUUID();
259          *         table.putItem(Item.builder()
260          *                           .putAttribute("partition-key", id)
261          *                           .putAttribute("creation-time", Instant.now())
262          *                           .build());
263          *
264          *         Item item = table.getItem(Item.builder()
265          *                                       .putAttribute("partition-key", id)
266          *                                       .build());
267          *
268          *         // Items are usually stored as a number, but it was stored as an ISO-8601 string now because of the
269          *         // InstantsAsStringsConverter.
270          *         assert item.attribute("creation-time").isString();
271          *         assert item.attribute("creation-time").as(Instant.class).isBetween(Instant.now().minus(1, MINUTE),
272          *                                                                            Instant.now());
273          *     }
274          * </code>
275          */
converters(List<ItemAttributeValueConverter<?>> converters)276         DocumentClientConfiguration.Builder converters(List<ItemAttributeValueConverter<?>> converters);
addConverter(ItemAttributeValueConverter<?> converter)277         DocumentClientConfiguration.Builder addConverter(ItemAttributeValueConverter<?> converter);
clearConverters()278         DocumentClientConfiguration.Builder clearConverters();
279 
280         /**
281          * Create the configuration object client with all of the configured values.
282          */
build()283         DocumentClientConfiguration build();
284     }
285 }
286 
287 /**
288  * A converter between Java types and DynamoDB types. These can be attached to {@link DynamoDbDocumentClient}s and
289  * {@link Item}s, so that types are automatically converted when writing to and reading from DynamoDB.
290  *
291  * @see DocumentClientConfiguration.Builder#converters(List)
292  * @see Item.Builder#converter(ItemAttributeValueConverter)
293  *
294  * @param T The Java type that is generated by this converter.
295  */
296 @ThreadSafe
297 public interface ItemAttributeValueConverter<T> {
298     /**
299      * The default condition in which this converter is invoked.
300      *
301      * Even if this condition is not satisfied, it can still be invoked directly via
302      * {@link ItemAttributeValue#convert(ItemAttributeValueConverter)}.
303      */
defaultConversionCondition()304     ConversionCondition defaultConversionCondition();
305 
306     /**
307      * Convert the provided Java type into an {@link ItemAttributeValue}.
308      */
toAttributeValue(T input, ConversionContext context)309     ItemAttributeValue toAttributeValue(T input, ConversionContext context);
310 
311     /**
312      * Convert the provided {@link ItemAttributeValue} into a Java type.
313      */
fromAttributeValue(ItemAttributeValue input, ConversionContext context)314     T fromAttributeValue(ItemAttributeValue input, ConversionContext context);
315 }
316 
317 /**
318  * The condition in which a {@link ItemAttributeValueConverter} will be invoked.
319  *
320  * @see ItemAttributeValueConverter#defaultConversionCondition().
321  */
322 @ThreadSafe
323 public interface ConversionCondition {
324     /**
325      * Create a conversion condition that causes an {@link ItemAttributeValueConverter} to be invoked if an attribute value's
326      * {@link ConversionContext} matches a specific condition.
327      *
328      * This condition has a larger overhead than the {@link #isInstanceOf(Class)} and {@link #never()}, because it must be
329      * invoked for every attribute value being converted and its result cannot be cached. For this reason, lower-overhead
330      * conditions like {@link #isInstanceOf(Class)} and {@link #never()} should be favored where performance is important.
331      */
contextSatisfies(Predicate<ConversionContext> contextPredicate)332     static ConversionCondition contextSatisfies(Predicate<ConversionContext> contextPredicate);
333 
334     /**
335      * Create a conversion condition that causes an {@link ItemAttributeValueConverter} to be invoked if the attribute value's
336      * Java type matches the provided class.
337      *
338      * The result of this condition can be cached, and will likely not be invoked for previously-converted types.
339      */
isInstanceOf(Class<?> clazz)340     static ConversionCondition isInstanceOf(Class<?> clazz);
341 
342     /**
343      * Create a conversion condition that causes an {@link ItemAttributeValueConverter} to never be invoked by default, except
344      * when directly invoked via {@link ItemAttributeValue#convert(ItemAttributeValueConverter)}.
345      *
346      * The result of this condition can be cached, and will likely not be invoked for previously-converted types.
347      */
never()348     static ConversionCondition never();
349 }
350 
351 /**
352  * Additional context that can be used in the context of converting between Java types and {@link ItemAttributeValue}s.
353  *
354  * @see ItemAttributeValueConverter#toAttributeValue(Object, ConversionContext)
355  * @see ItemAttributeValueConverter#fromAttributeValue(ItemAttributeValue, ConversionContext)
356  */
357 @ThreadSafe
358 public interface ConversionContext {
359     /**
360      * The name of the attribute being converted.
361      */
attributeName()362     String attributeName();
363 
364     /**
365      * The schema of the attribute being converted.
366      */
attributeSchema()367     ItemAttributeSchema attributeSchema();
368 
369     /**
370      * The item that contains the attribute being converted.
371      */
parent()372     Item parent();
373 
374     /**
375      * The schema of the {@link #parent()}.
376      */
parentSchema()377     ItemSchema parentSchema();
378 }
379 
380 /**
381  * The result of invoking {@link DynamoDbDocumentClient#listTables()}.
382  */
383 @ThreadSafe
384 public interface ListTablesResponse {
385     /**
386      * A lazily-populated iterator over all tables in the current region. This may make multiple service calls in the
387      * background when iterating over the full result set.
388      */
tables()389     SdkIterable<Table> tables();
390 }
391 
392 /**
393  * A DynamoDB table, containing a collection of {@link Item}s.
394  *
395  * Currently supported operations:
396  * <ul>
397  *     <li>Writing objects with {@link #putItem(Item)} and {@link #putObject(Object)}</li>
398  *     <li>Reading objects with {@link #getItem(Item)}} and {@link #getObject(Object)}</li>
399  *     <li>Accessing the current table configuration with {@link #metadata()}.</li>
400  *     <li>Creating new indexes with {@link #createGlobalSecondaryIndex(CreateGlobalSecondaryIndexRequest)}.</li>
401  * </ul>
402  *
403  * The full version will all table operations, including Query, Delete, Update, Scan, etc.
404  */
405 @ThreadSafe
406 public interface Table {
407     /**
408      * Retrieve the name of this table.
409      */
name()410     String name();
411 
412     /**
413      * Invoke DynamoDB to retrieve the metadata for this table.
414      */
metadata()415     TableMetadata metadata();
416 
417     /**
418      * Invoke DynamoDB to create a new global secondary index for this table.
419      *
420      * Usage Example:
421      * <code>
422      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
423      *         ProvisionedCapacity indexCapacity = ProvisionedCapacity.builder()
424      *                                                                .readCapacity(5)
425      *                                                                .writeCapacity(5)
426      *                                                                .build();
427      *
428      *         KeySchema indexKeys = KeySchema.builder()
429      *                                        .putKey("extra-partition-key", ItemAttributeIndexType.PARTITION_KEY)
430      *                                        .build();
431      *
432      *         Table table = client.getTable("my-table");
433      *
434      *         table.createGlobalSecondaryIndex(CreateGlobalSecondaryIndexRequest.builder()
435      *                                                                           .indexName("my-new-index")
436      *                                                                           .provisionedCapacity(tableCapacity)
437      *                                                                           .keySchema(tableKeys)
438      *                                                                           .build());
439      *     }
440      * </code>
441      */
createGlobalSecondaryIndex(CreateGlobalSecondaryIndexRequest createRequest)442     CreateGlobalSecondaryIndexResponse createGlobalSecondaryIndex(CreateGlobalSecondaryIndexRequest createRequest);
443 
444     /**
445      * Invoke DynamoDB to create or override an {@link Item} in this table.
446      *
447      * This method is optimized for performance, and provides no additional response data. For additional options
448      * like conditions or consumed capacity, see {@link #putItem(PutItemRequest)}.
449      *
450      * Usage Example:
451      * <code>
452      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
453      *         Table table = client.getTable("my-table");
454      *         table.putItem(Item.builder()
455      *                           .putAttribute("partition-key", UUID.randomUUID())
456      *                           .putAttribute("creation-time", Instant.now())
457      *                           .build());
458      *     }
459      * </code>
460      */
putItem(Item item)461     void putItem(Item item);
462 
463     /**
464      * Invoke DynamoDB to create or override an {@link Item} in this table.
465      *
466      * This method provides more options than {@link #putItem(Item)}, like conditions or consumed capacity.
467      *
468      * Usage Example:
469      * <code>
470      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
471      *         Table table = client.getTable("my-table");
472      *         table.putItem(PutItemRequest.builder()
473      *                                     .item(Item.builder()
474      *                                               .putAttribute("partition-key", "key")
475      *                                               .putAttribute("version", 2)
476      *                                               .build())
477      *                                     .condition("version = :expected_version")
478      *                                     .putConditionAttribute(":expected_version", 1)
479      *                                     .build());
480      *     } catch (ConditionFailedException e) {
481      *         System.out.println("Precondition failed.");
482      *         throw e;
483      *     }
484      * </code>
485      */
putItem(PutItemRequest putRequest)486     PutItemResponse putItem(PutItemRequest putRequest)
487             throws ConditionFailedException;
488 
489     /**
490      * Invoke DynamoDB to create or override an Item in this table.
491      *
492      * This will convert the provided object into an {@link Item} automatically using the default Object-to-Item
493      * {@link ItemAttributeValueConverter}, unless an alternate converter has been overridden for the provided type.
494      *
495      * This method is optimized for performance, and provides no additional response data. For additional options
496      * like conditions or consumed capacity, see {@link #putObject(PutObjectRequest)}.
497      *
498      * Usage Example:
499      * <code>
500      *     public class MyItem {
501      *         @Attribute("partition-key")
502      *         @Index(AttributeIndexType.PARTITION_KEY)
503      *         private String partitionKey;
504      *
505      *         @Attribute("creation-time")
506      *         private Instant creationTime;
507      *
508      *         public String getPartitionKey() { return this.partitionKey; }
509      *         public Instant getCreationTime() { return this.creationTime; }
510      *         public void setPartitionKey(String partitionKey) { this.partitionKey = partitionKey; }
511      *         public void setCreationTime(Instant creationTime) { this.creationTime = creationTime; }
512      *     }
513      *
514      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
515      *         Table table = client.getTable("my-table");
516      *
517      *         MyItem myItem = new MyItem();
518      *         myItem.setPartitionKey(UUID.randomUUID());
519      *         myItem.setCreationTime(Instant.now());
520      *
521      *         table.putObject(myItem);
522      *     }
523      * </code>
524      */
putObject(Object item)525     void putObject(Object item);
526 
527     /**
528      * Invoke DynamoDB to create or override an Item in this table.
529      *
530      * This will convert the provided object into an {@link Item} automatically using the default Object-to-Item
531      * {@link ItemAttributeValueConverter}, unless an alternate converter has been overridden for the provided type.
532      *
533      * This method provides more options than {@link #putObject(Object)} like conditions or consumed capacity.
534      *
535      * Usage Example:
536      * <code>
537      *     public class MyItem {
538      *         @Attribute("partition-key")
539      *         @Index(AttributeIndexType.PARTITION_KEY)
540      *         private String partitionKey;
541      *
542      *         @Attribute
543      *         private int version;
544      *
545      *         public String getPartitionKey() { return this.partitionKey; }
546      *         public int getVersion() { return this.version; }
547      *         public void setPartitionKey(String partitionKey) { this.partitionKey = partitionKey; }
548      *         public void setVersion(int version) { this.version = version; }
549      *     }
550      *
551      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
552      *         Table table = client.getTable("my-table");
553      *
554      *         MyItem myItem = new MyItem();
555      *         myItem.setPartitionKey(UUID.randomUUID());
556      *         myItem.setVersion(2);
557      *
558      *         table.putObject(PutObjectRequest.builder(myItem)
559      *                                         .condition("version = :expected_version")
560      *                                         .putConditionAttribute(":expected_version", 1)
561      *                                         .build());
562      *     } catch (ConditionFailedException e) {
563      *         System.out.println("Precondition failed.");
564      *         throw e;
565      *     }
566      * </code>
567      */
putObject(PutObjectRequest<T> putRequest)568     <T> PutObjectResponse<T> putObject(PutObjectRequest<T> putRequest)
569             throws ConditionFailedException;
570 
571     /**
572      * Invoke DynamoDB to retrieve an Item in this table, based on its partition key (and sort key, if the table has one).
573      *
574      * This method is optimized for performance, and provides no additional response data. For additional options
575      * like consistent reads, see {@link #getItem(GetItemRequest)}.
576      *
577      * Usage Example:
578      * <code>
579      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
580      *         Table table = client.getTable("my-table");
581      *         UUID id = UUID.randomUUID();
582      *         table.putItem(Item.builder()
583      *                           .putAttribute("partition-key", id)
584      *                           .putAttribute("creation-time", Instant.now())
585      *                           .build());
586      *
587      *         // Wait a little bit, because getItem is eventually consistent by default.
588      *         Thread.sleep(5_000);
589      *
590      *         Item item = table.getItem(Item.builder()
591      *                                       .putAttribute("partition-key", id)
592      *                                       .build());
593      *
594      *         // Times are stored as numbers, by default, so they can also be used as sort keys.
595      *         assert item.attribute("creation-time").isNumber();
596      *         assert item.attribute("creation-time").as(Instant.class).isBetween(Instant.now().minus(1, MINUTE),
597      *                                                                            Instant.now());
598      *     } catch (NoSuchItemException e) {
599      *         System.out.println("Item could not be found. Maybe we didn't wait long enough for consistency?");
600      *         throw e;
601      *     }
602      * </code>
603      */
getItem(Item item)604     Item getItem(Item item)
605             throws NoSuchItemException;
606 
607     /**
608      * Invoke DynamoDB to retrieve an Item in this table, based on its partition key (and sort key, if table has one).
609      *
610      * This method provides more options than {@link #getItem(Item)}, like whether reads should be consistent.
611      *
612      * Usage Example:
613      * <code>
614      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
615      *         Table table = client.getTable("my-table");
616      *         UUID id = UUID.randomUUID();
617      *         table.putItem(Item.builder()
618      *                           .putAttribute("partition-key", id)
619      *                           .putAttribute("creation-time", Instant.now())
620      *                           .build());
621      *
622      *         GetItemResponse response =
623      *                 table.getItem(GetItemRequest.builder()
624      *                                             .item(Item.builder()
625      *                                                       .putAttribute("partition-key", id)
626      *                                                       .build())
627      *                                             .consistentRead(true)
628      *                                             .build());
629      *
630      *         // Times are stored as numbers, by default, so they can also be used as sort keys.
631      *         assert response.item().attribute("creation-time").isNumber();
632      *         assert response.item().attribute("creation-time").as(Instant.class).isBetween(Instant.now().minus(1, MINUTE),
633      *                                                                                       Instant.now());
634      *     } catch (NoSuchItemException e) {
635      *         System.out.println("Item was deleted between creation and retrieval.");
636      *         throw e;
637      *     }
638      * </code>
639      */
getItem(GetItemRequest getRequest)640     GetItemResponse getItem(GetItemRequest getRequest)
641             throws NoSuchItemException;
642 
643     /**
644      * Invoke DynamoDB to retrieve an Item in this table.
645      *
646      * This will use the partition and sort keys from the provided object and convert the DynamoDB response to a Java object
647      * automatically using the default Object-to-Item {@link ItemAttributeValueConverter}, unless an alternate converter
648      * has been overridden for the provided type.
649      *
650      * This method is optimized for performance, and provides no additional response data. For additional options
651      * like consistent reads, see {@link #getObject(GetObjectRequest)}.
652      *
653      * Usage Example:
654      * <code>
655      *     public class MyItem {
656      *         @Attribute("partition-key")
657      *         @Index(AttributeIndexType.PARTITION_KEY)
658      *         private String partitionKey;
659      *
660      *         @Attribute("creation-time")
661      *         private Instant creationTime;
662      *
663      *         public String getPartitionKey() { return this.partitionKey; }
664      *         public Instant getCreationTime() { return this.creationTime; }
665      *         public void setPartitionKey(String partitionKey) { this.partitionKey = partitionKey; }
666      *         public void setCreationTime(Instant creationTime) { this.creationTime = creationTime; }
667      *     }
668      *
669      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
670      *         Table table = client.getTable("my-table");
671      *
672      *         UUID id = UUID.randomUUID();
673      *
674      *         MyItem itemToCreate = new MyItem();
675      *         itemToCreate.setPartitionKey(id);
676      *         itemToCreate.setCreationTime(Instant.now());
677      *
678      *         table.putObject(itemToCreate);
679      *
680      *         // Wait a little bit, because getObject is eventually consistent by default.
681      *         Thread.sleep(5_000);
682      *
683      *         MyItem itemToRetrieve = new MyItem();
684      *         itemToRetrieve.setPartitionKey(id);
685      *
686      *         MyItem retrievedItem = table.getObject(itemToRetrieve);
687      *         assert retrievedItem.getCreationTime().isBetween(Instant.now().minus(1, MINUTE),
688      *                                                          Instant.now());
689      *     } catch (NoSuchItemException e) {
690      *         System.out.println("Item could not be found. Maybe we didn't wait long enough for consistency?");
691      *         throw e;
692      *     }
693      * </code>
694      */
getObject(T item)695     <T> T getObject(T item)
696             throws NoSuchItemException;
697 
698     /**
699      * Invoke DynamoDB to retrieve an Item in this table.
700      *
701      * This will use the partition and sort keys from the provided object and convert the DynamoDB response to a Java object
702      * automatically using the default Object-to-Item {@link ItemAttributeValueConverter}, unless an alternate converter
703      * has been overridden for the provided type.
704      *
705      * This method provides more options than {@link #getObject(GetObjectRequest)}, like whether reads should be consistent.
706      *
707      * Usage Example:
708      * <code>
709      *     public class MyItem {
710      *         @Attribute("partition-key")
711      *         @Index(AttributeIndexType.PARTITION_KEY)
712      *         private String partitionKey;
713      *
714      *         @Attribute("creation-time")
715      *         private Instant creationTime;
716      *
717      *         public String getPartitionKey() { return this.partitionKey; }
718      *         public Instant getCreationTime() { return this.creationTime; }
719      *         public void setPartitionKey(String partitionKey) { this.partitionKey = partitionKey; }
720      *         public void setCreationTime(Instant creationTime) { this.creationTime = creationTime; }
721      *     }
722      *
723      *     try (DynamoDbDocumentClient client = DynamoDb.documentClient()) {
724      *         Table table = client.getTable("my-table");
725      *
726      *         UUID id = UUID.randomUUID();
727      *
728      *         MyItem itemToCreate = new MyItem();
729      *         itemToCreate.setPartitionKey(id);
730      *         itemToCreate.setCreationTime(Instant.now());
731      *
732      *         table.putObject(itemToCreate);
733      *
734      *         MyItem itemToRetrieve = new MyItem();
735      *         itemToRetrieve.setPartitionKey(id);
736      *
737      *         GetObjectResponse<MyItem> response = table.getObject(GetObjectRequest.builder(itemToRetrieve)
738      *                                                                              .consistentReads(true)
739      *                                                                              .build());
740      *         MyItem retrievedItem = response.item();
741      *         assert retrievedItem.getCreationTime().isBetween(Instant.now().minus(1, MINUTE),
742      *                                                          Instant.now());
743      *     } catch (NoSuchItemException e) {
744      *         System.out.println("Item was deleted between creation and retrieval.");
745      *         throw e;
746      *     }
747      * </code>
748      */
getObject(GetObjectRequest<T> getRequest)749     <T> GetObjectResponse<T> getObject(GetObjectRequest<T> getRequest)
750             throws NoSuchItemException;
751 }
752 
753 /**
754  * Additional information about a {@link Table}, retrieved via {@link Table#metadata()}.
755  */
756 @ThreadSafe
757 public interface TableMetadata {
758     /**
759      * All global secondary indexes that can be used for querying or retrieving from the table.
760      */
globalSecondaryIndexMetadata()761     List<GlobalSecondaryIndexMetadata> globalSecondaryIndexMetadata();
762 
763     /**
764      * All local secondary indexes that can be used for querying or retrieving from the table.
765      */
localSecondaryIndexMetadata()766     List<LocalSecondaryIndexMetadata> localSecondaryIndexMetadata();
767 }
768 
769 /**
770  * An item in a {@link Table}. This is similar to a "row" in a traditional relational database.
771  *
772  * In the following table, { "User ID": 1, "Username": "joe" } is an item:
773  *
774  * <pre>
775  * Table: Users
776  * | ------------------ |
777  * | User ID | Username |
778  * | ------------------ |
779  * |       1 |      joe |
780  * |       2 |     jane |
781  * | ------------------ |
782  * </pre>
783  */
784 @ThreadSafe
785 public interface Item {
786     /**
787      * Create a builder for configuring and creating a {@link Item}.
788      */
builder()789     static Item.Builder builder();
790 
791     /**
792      * Retrieve all {@link ItemAttributeValue}s in this item.
793      */
attributes()794     Map<String, ItemAttributeValue> attributes();
795 
796     /**
797      * Retrieve a specific attribute from this item.
798      */
attribute(String attributeKey)799     ItemAttributeValue attribute(String attributeKey);
800 
801     interface Builder {
802         /**
803          * Add an attribute to this item. The methods accepting "Object", will be converted using the default
804          * {@link ItemAttributeValueConverter}s.
805          */
putAttribute(String attributeKey, ItemAttributeValue attributeValue)806         Item.Builder putAttribute(String attributeKey, ItemAttributeValue attributeValue);
putAttribute(String attributeKey, ItemAttributeValue attributeValue, ItemAttributeSchema attributeSchema)807         Item.Builder putAttribute(String attributeKey, ItemAttributeValue attributeValue, ItemAttributeSchema attributeSchema);
putAttribute(String attributeKey, Object attributeValue)808         Item.Builder putAttribute(String attributeKey, Object attributeValue);
putAttribute(String attributeKey, Object attributeValue, ItemAttributeSchema attributeSchema)809         Item.Builder putAttribute(String attributeKey, Object attributeValue, ItemAttributeSchema attributeSchema);
removeAttribute(String attributeKey)810         Item.Builder removeAttribute(String attributeKey);
clearAttributes()811         Item.Builder clearAttributes();
812 
813         /**
814          * Add converters that should be used for this item and its attributes. These converters are used with a higher
815          * precidence than those configured in the {@link DocumentClientConfiguration}.
816          *
817          * See {@link DocumentClientConfiguration.Builder#addConverter(ItemAttributeValueConverter)} for example usage.
818          */
converters(List<ItemAttributeValueConverter<?>> converters)819         Item.Builder converters(List<ItemAttributeValueConverter<?>> converters);
addConverter(ItemAttributeValueConverter<?> converter)820         Item.Builder addConverter(ItemAttributeValueConverter<?> converter);
clearConverters()821         Item.Builder clearConverters();
822 
823         /**
824          * Create an {@link Item} using the current configuration on the builder.
825          */
build()826         Item build();
827     }
828 }
829 
830 /**
831  * The value of an attribute within an {@link Item}. In a traditional relational database, this would be analogous to a cell
832  * in the table.
833  *
834  * In the following table, "joe" and "jane" are both attribute values:
835  * <pre>
836  * Table: Users
837  * | ------------------ |
838  * | User ID | Username |
839  * | ------------------ |
840  * |       1 |      joe |
841  * |       2 |     jane |
842  * | ------------------ |
843  * </pre>
844  */
845 @ThreadSafe
846 public interface ItemAttributeValue {
847     /**
848      * Create an {@link ItemAttributeValue} from the provided object.
849      */
from(Object object)850     static ItemAttributeValue from(Object object);
851 
852     /**
853      * Create an {@link ItemAttributeValue} from the provided object, and associate this value with the provided
854      * {@link ItemAttributeValueConverter}. This allows it to be immediately converted with {@link #as(Class)}.
855      *
856      * This is equivalent to {@code ItemAttributeValue.from(object).convertFromJavaType(converter)}.
857      */
from(Object object, ItemAttributeValueConverter<?> converter)858     static ItemAttributeValue from(Object object, ItemAttributeValueConverter<?> converter);
859 
860     /**
861      * Create an {@link ItemAttributeValue} that represents the DynamoDB-specific null type.
862      */
nullValue()863     static ItemAttributeValue nullValue();
864 
865     /**
866      * Convert this item attribute value into the requested Java type.
867      *
868      * This uses the {@link ItemAttributeValueConverter} configured on this type via
869      * {@link #from(Object, ItemAttributeValueConverter)} or {@link #convertFromJavaType(ItemAttributeValueConverter)}.
870      */
as(Class<T> type)871     <T> T as(Class<T> type);
872 
873     /**
874      * Retrieve the {@link ItemAttributeValueType} of this value.
875      */
type()876     ItemAttributeValueType type();
877 
878     /**
879      * The {@code is*} methods can be used to check the underlying DynamoDB-specific type of the attribute value.
880      *
881      * If the type isn't known (eg. because it was created via {@link ItemAttributeValue#from(Object)}), {@link #isJavaType()}
882      * will return true. Such types will be converted into DynamoDB-specific types by the document client before they are
883      * persisted.
884      */
885 
isItem()886     boolean isItem();
isString()887     boolean isString();
isNumber()888     boolean isNumber();
isBytes()889     boolean isBytes();
isBoolean()890     boolean isBoolean();
isListOfStrings()891     boolean isListOfStrings();
isListOfNumbers()892     boolean isListOfNumbers();
isListOfBytes()893     boolean isListOfBytes();
isListOfAttributeValues()894     boolean isListOfAttributeValues();
isNull()895     boolean isNull();
isJavaType()896     boolean isJavaType();
897 
898     /**
899      * The {@code as*} methods can be used to retrieve this value without the overhead of type conversion of {@link #as(Class)}.
900      *
901      * An exception will be thrown from these methods if the requested type does not match the actual underlying type. When
902      * the type isn't know, the {@code is*} or {@link #type()} methods can be used to query the underlying type before
903      * invoking these {@code as*} methods.
904      */
asItem()905     Item asItem();
asString()906     String asString();
asNumber()907     BigDecimal asNumber();
asBytes()908     SdkBytes asBytes();
asBoolean()909     Boolean asBoolean();
asListOfStrings()910     List<String> asListOfStrings();
asListOfNumbers()911     List<BigDecimal> asListOfNumbers();
asListOfBytes()912     List<SdkBytes> asListOfBytes();
asListOfAttributeValues()913     List<ItemAttributeValue> asListOfAttributeValues();
asJavaType()914     Object asJavaType();
915 
916     /**
917      * Convert this attribute value from a {@link ItemAttributeValueType#JAVA_TYPE} to a type that can be persisted in DynamoDB.
918      *
919      * This will throw an exception if {@link #isJavaType()} is false.
920      */
convertFromJavaType(ItemAttributeValueConverter<?> converter)921     ItemAttributeValue convertFromJavaType(ItemAttributeValueConverter<?> converter);
922 }
923 
924 /**
925  * The schema for a specific item. This describes the item's structure and which attributes it contains.
926  *
927  * This is mostly an implementation detail, and can be ignored except by developers interested in creating
928  * {@link ItemAttributeValueConverter}.
929  */
930 @ThreadSafe
931 public interface ItemSchema {
932     /**
933      * Create a builder for configuring and creating an {@link ItemSchema}.
934      */
builder()935     static ItemSchema.Builder builder();
936 
937     interface Builder {
938         /**
939          * Specify the attribute schemas that describe each attribute of this item.
940          */
attributeSchemas(Map<String, ItemAttributeSchema> attributeSchemas)941         ItemSchema.Builder attributeSchemas(Map<String, ItemAttributeSchema> attributeSchemas);
putAttributeSchema(String attributeName, ItemAttributeSchema attributeSchema)942         ItemSchema.Builder putAttributeSchema(String attributeName, ItemAttributeSchema attributeSchema);
removeAttributeSchema(String attributeName)943         ItemSchema.Builder removeAttributeSchema(String attributeName);
clearAttributeSchemas()944         ItemSchema.Builder clearAttributeSchemas();
945 
946         /**
947          * The converter that should be used for converting all items that conform to this schema.
948          */
converter(ItemAttributeValueConverter<?> converter)949         ItemSchema.Builder converter(ItemAttributeValueConverter<?> converter);
950 
951         /**
952          * Create an {@link ItemSchema} using the current configuration on the builder.
953          */
build()954         ItemSchema build();
955     }
956 }
957 
958 /**
959  * The schema for a specific item attribute. This describes the attribute's structure, including whether it is known to be an
960  * index, what the Java-specific type representation is for this attribute, etc.
961  *
962  * This is mostly an implementation detail, and can be ignored except by developers interested in creating
963  * {@link ItemAttributeValueConverter}.
964  */
965 @ThreadSafe
966 public interface ItemAttributeSchema {
967     /**
968      * Create a builder for configuring and creating an {@link ItemAttributeSchema}.
969      */
builder()970     static ItemAttributeSchema.Builder builder();
971 
972     interface Builder {
973         /**
974          * Specify whether this field is known to be an index.
975          */
indexType(AttributeIndexType attributeIndexType)976         ItemAttributeSchema.Builder indexType(AttributeIndexType attributeIndexType);
977 
978         /**
979          * Specify the Java-specific type representation for this type.
980          */
javaType(Class<?> attributeJavaType)981         ItemAttributeSchema.Builder javaType(Class<?> attributeJavaType);
982 
983         /**
984          * The DynamoDB-specific type representation for this type.
985          */
dynamoType(ItemAttributeValueType attributeDynamoType)986         ItemAttributeSchema.Builder dynamoType(ItemAttributeValueType attributeDynamoType);
987 
988         /**
989          * The converter that should be used for converting all items that conform to this schema.
990          */
converter(ItemAttributeValueConverter<?> converter)991         ItemAttributeSchema.Builder converter(ItemAttributeValueConverter<?> converter);
992 
993         /**
994          * Create an {@link ItemAttributeSchema} using the current configuration on the builder.
995          */
build()996         ItemAttributeSchema build();
997     }
998 }
999 
1000 /**
1001  * The index type of an {@link ItemAttributeValue}.
1002  */
1003 @ThreadSafe
1004 public enum ItemAttributeIndexType {
1005     PARTITION_KEY,
1006     SORT_KEY,
1007     NOT_AN_INDEX
1008 }
1009 
1010 /**
1011  * The underlying type of an {@link ItemAttributeValue}.
1012  */
1013 @ThreadSafe
1014 public enum ItemAttributeValueType {
1015     ITEM,
1016     STRING,
1017     NUMBER,
1018     BYTES,
1019     BOOLEAN,
1020     LIST_OF_STRINGS,
1021     LIST_OF_NUMBERS,
1022     LIST_OF_BYTES,
1023     LIST_OF_ATTRIBUTE_VALUES,
1024     NULL,
1025     JAVA_TYPE
1026 }