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 }