Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Découvrez les bases du client DynamoDB Enhanced API
Cette rubrique décrit les fonctionnalités de base du client DynamoDB amélioré et les compare API au client DynamoDB standard
Si vous utilisez le API client DynamoDB Enhanced pour la première fois, nous vous recommandons de suivre le didacticiel d'introduction pour vous familiariser avec les classes fondamentales.
Éléments DynamoDB en Java
Les tables DynamoDB stockent des éléments. Selon votre cas d'utilisation, les éléments du côté Java peuvent prendre la forme de données structurées statiquement ou de structures créées dynamiquement.
Si votre cas d'utilisation nécessite des éléments dotés d'un ensemble cohérent d'attributs, utilisez des classes annotées ou utilisez un générateur pour générer le type statique TableSchema
approprié.
Sinon, si vous devez stocker des éléments composés de différentes structures, créez unDocumentTableSchema
. DocumentTableSchema
fait partie du document amélioré API et ne nécessite qu'une clé primaire de type statique et fonctionne avec des EnhancedDocument
instances pour contenir les éléments de données. Le document amélioré API est traité dans une autre rubrique.
Types d'attributs pour les classes de modèles de données
Bien que DynamoDB prenne en charge un petit nombre de types d'attributs par rapport au système de types riches de Java, le API client amélioré DynamoDB fournit des mécanismes permettant de convertir les membres d'une classe Java vers et depuis les types d'attributs DynamoDB.
Les types d'attributs (propriétés) de vos classes de données Java doivent être des types d'objets et non des primitives. Par exemple, utilisez toujours des types de données Long
et des Integer
objets, long
et non des int
primitives.
Par défaut, le API client DynamoDB Enhanced prend en charge les convertisseurs d'attributs pour un grand nombre de types, tels que
Pour stocker les données d'un type d'attribut qui n'est pas pris en charge par défaut ou qui n'est pas conforme à la JavaBean convention, vous pouvez écrire une AttributeConverter
implémentation personnalisée pour effectuer la conversion. Consultez la section sur la conversion d'attributs pour un exemple.
Pour stocker les données d'un type d'attribut dont la classe est conforme à la spécification Java Beans (ou d'une classe de données immuable), vous pouvez adopter deux approches.
-
Si vous avez accès au fichier source, vous pouvez annoter la classe avec
@DynamoDbBean
(ou@DynamoDbImmutable
). La section qui traite des attributs imbriqués présente des exemples d'utilisation de classes annotées.
-
Si vous n'avez pas accès au fichier source de la classe de JavaBean données pour l'attribut (ou si vous ne souhaitez pas annoter le fichier source d'une classe à laquelle vous avez accès), vous pouvez utiliser l'approche du générateur. Cela crée un schéma de table sans définir les clés. Vous pouvez ensuite imbriquer ce schéma de table dans un autre schéma de table pour effectuer le mappage. La section des attributs imbriqués contient un exemple illustrant l'utilisation de schémas imbriqués.
Valeurs nulles
Lorsque vous utilisez putItem
cette méthode, le client amélioré n'inclut pas les attributs à valeur nulle d'un objet de données mappé dans la demande adressée à DynamoDB.
Le comportement par défaut SDK de the pour les updateItem
demandes supprime les attributs de l'élément de DynamoDB qui sont définis sur null dans l'objet que vous soumettez dans la méthode. updateItem
Si vous avez l'intention de mettre à jour certaines valeurs d'attributs et de conserver les autres inchangées, deux options s'offrent à vous.
-
Récupérez l'élément (en utilisant
getItem
) avant de modifier les valeurs. En utilisant cette approche, toutes les valeurs anciennes et mises à jour sont soumises à DynamoDB. SDK -
Utilisez le
IgnoreNullsMode
ou.SCALAR_ONLY IgnoreNullsMode.MAPS_ONLY
lorsque vous créez la demande pour mettre à jour l'élément. Les deux modes ignorent les propriétés à valeur nulle de l'objet qui représentent des attributs scalaires dans DynamoDB. La Mettre à jour les éléments contenant des types complexes rubrique de ce guide contient plus d'informations sur lesIgnoreNullsMode
valeurs et sur la manière de travailler avec des types complexes.
L'exemple suivant illustre ignoreNullsMode()
la updateItem()
méthode.
public static void updateItemNullsExample() { Customer customer = new Customer(); customer.setCustName("CustomerName"); customer.setEmail("email"); customer.setId("1"); customer.setRegistrationDate(Instant.now()); logger.info("Original customer: {}", customer); // Put item with values for all attributes. try { customerAsyncDynamoDbTable.putItem(customer).join(); } catch (RuntimeException rte) { logger.error("A exception occurred during putItem: {}", rte.getCause().getMessage(), rte); return; } // Create a Customer instance with the same 'id' and 'email' values, but a different 'name' value. // Do not set the 'registrationDate' attribute. Customer customerForUpdate = new Customer(); customerForUpdate.setCustName("NewName"); customerForUpdate.setEmail("email"); customerForUpdate.setId("1"); // Update item without setting the 'registrationDate' property and set IgnoreNullsMode to SCALAR_ONLY. try { Customer updatedWithNullsIgnored = customerAsyncDynamoDbTable.updateItem(b -> b .item(customerForUpdate) .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY)) .join(); logger.info("Customer updated with nulls ignored: {}", updatedWithNullsIgnored.toString()); } catch (RuntimeException rte) { logger.error("An exception occurred during updateItem: {}", rte.getCause().getMessage(), rte); return; } // Update item without setting the registrationDate attribute and not setting ignoreNulls to true. try { Customer updatedWithNullsUsed = customerAsyncDynamoDbTable.updateItem(customerForUpdate) .join(); logger.info("Customer updated with nulls used: {}", updatedWithNullsUsed.toString()); } catch (RuntimeException rte) { logger.error("An exception occurred during updateItem: {}", rte.getCause().getMessage(), rte); } } // Logged lines. Original customer: Customer [id=1, name=CustomerName, email=email, regDate=2024-10-11T14:12:30.222858Z] Customer updated with nulls ignored: Customer [id=1, name=NewName, email=email, regDate=2024-10-11T14:12:30.222858Z] Customer updated with nulls used: Customer [id=1, name=NewName, email=email, regDate=null]
Méthodes de base du client DynamoDB Enhanced
Les méthodes de base du client amélioré correspondent aux opérations de service DynamoDB dont elles portent le nom. Les exemples suivants montrent la variante la plus simple de chaque méthode. Vous pouvez personnaliser chaque méthode en transmettant un objet de demande amélioré. Les objets de requête améliorés offrent la plupart des fonctionnalités disponibles dans le client DynamoDB standard. Ils sont entièrement documentés dans la AWS SDK for Java 2.x API référence.
L'exemple utilise ce qui classe Customer est indiqué précédemment.
// CreateTable customerTable.createTable(); // GetItem Customer customer = customerTable.getItem(Key.builder().partitionValue("a123").build()); // UpdateItem Customer updatedCustomer = customerTable.updateItem(customer); // PutItem customerTable.putItem(customer); // DeleteItem Customer deletedCustomer = customerTable.deleteItem(Key.builder().partitionValue("a123").sortValue(456).build()); // Query PageIterable<Customer> customers = customerTable.query(keyEqualTo(k -> k.partitionValue("a123"))); // Scan PageIterable<Customer> customers = customerTable.scan(); // BatchGetItem BatchGetResultPageIterable batchResults = enhancedClient.batchGetItem(r -> r.addReadBatch(ReadBatch.builder(Customer.class) .mappedTableResource(customerTable) .addGetItem(key1) .addGetItem(key2) .addGetItem(key3) .build())); // BatchWriteItem batchResults = enhancedClient.batchWriteItem(r -> r.addWriteBatch(WriteBatch.builder(Customer.class) .mappedTableResource(customerTable) .addPutItem(customer) .addDeleteItem(key1) .addDeleteItem(key1) .build())); // TransactGetItems transactResults = enhancedClient.transactGetItems(r -> r.addGetItem(customerTable, key1) .addGetItem(customerTable, key2)); // TransactWriteItems enhancedClient.transactWriteItems(r -> r.addConditionCheck(customerTable, i -> i.key(orderKey) .conditionExpression(conditionExpression)) .addUpdateItem(customerTable, customer) .addDeleteItem(customerTable, key));
Comparez le client DynamoDB amélioré au client DynamoDB standard
Les APIs clients DynamoDB (standard et
Bien que les deux clients APIs prennent en charge les opérations au niveau des données, le client DynamoDB standard prend également en charge les opérations au niveau des ressources. Les opérations au niveau des ressources gèrent la base de données, telles que la création de sauvegardes, la liste des tables et la mise à jour des tables. Le client amélioré API prend en charge un certain nombre d'opérations au niveau des ressources, telles que la création, la description et la suppression de tables.
Pour illustrer les différentes approches utilisées par les deux clientsAPIs, les exemples de code suivants montrent la création de la même ProductCatalog
table à l'aide du client standard et du client amélioré.
Comparaison : création d'une table à l'aide du client DynamoDB standard
DependencyFactory.dynamoDbClient().createTable(builder -> builder .tableName(TABLE_NAME) .attributeDefinitions( b -> b.attributeName("id").attributeType(ScalarAttributeType.N), b -> b.attributeName("title").attributeType(ScalarAttributeType.S), b -> b.attributeName("isbn").attributeType(ScalarAttributeType.S) ) .keySchema( builder1 -> builder1.attributeName("id").keyType(KeyType.HASH), builder2 -> builder2.attributeName("title").keyType(KeyType.RANGE) ) .globalSecondaryIndexes(builder3 -> builder3 .indexName("products_by_isbn") .keySchema(builder2 -> builder2 .attributeName("isbn").keyType(KeyType.HASH)) .projection(builder2 -> builder2 .projectionType(ProjectionType.INCLUDE) .nonKeyAttributes("price", "authors")) .provisionedThroughput(builder4 -> builder4 .writeCapacityUnits(5L).readCapacityUnits(5L)) ) .provisionedThroughput(builder1 -> builder1 .readCapacityUnits(5L).writeCapacityUnits(5L)) );
Comparaison : création d'une table à l'aide du client DynamoDB Enhanced
DynamoDbEnhancedClient enhancedClient = DependencyFactory.enhancedClient(); productCatalog = enhancedClient.table(TABLE_NAME, TableSchema.fromImmutableClass(ProductCatalog.class)); productCatalog.createTable(b -> b .provisionedThroughput(b1 -> b1.readCapacityUnits(5L).writeCapacityUnits(5L)) .globalSecondaryIndices(b2 -> b2.indexName("products_by_isbn") .projection(b4 -> b4 .projectionType(ProjectionType.INCLUDE) .nonKeyAttributes("price", "authors")) .provisionedThroughput(b3 -> b3.writeCapacityUnits(5L).readCapacityUnits(5L)) ) );
Le client amélioré utilise la classe de données annotée suivante. Le client DynamoDB Enhanced fait correspondre les types de données Java aux types de données DynamoDB pour un code moins détaillé et plus facile à suivre. ProductCatalog
est un exemple d'utilisation d'une classe immuable avec le client DynamoDB Enhanced. L'utilisation de classes immuables pour les classes de données mappées est abordée plus loin dans cette rubrique.
package org.example.tests.model; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbIgnore; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey; import java.math.BigDecimal; import java.util.Objects; import java.util.Set; @DynamoDbImmutable(builder = ProductCatalog.Builder.class) public class ProductCatalog implements Comparable<ProductCatalog> { private Integer id; private String title; private String isbn; private Set<String> authors; private BigDecimal price; private ProductCatalog(Builder builder){ this.authors = builder.authors; this.id = builder.id; this.isbn = builder.isbn; this.price = builder.price; this.title = builder.title; } public static Builder builder(){ return new Builder(); } @DynamoDbPartitionKey public Integer id() { return id; } @DynamoDbSortKey public String title() { return title; } @DynamoDbSecondaryPartitionKey(indexNames = "products_by_isbn") public String isbn() { return isbn; } public Set<String> authors() { return authors; } public BigDecimal price() { return price; } public static final class Builder { private Integer id; private String title; private String isbn; private Set<String> authors; private BigDecimal price; private Builder(){} public Builder id(Integer id) { this.id = id; return this; } public Builder title(String title) { this.title = title; return this; } public Builder isbn(String ISBN) { this.isbn = ISBN; return this; } public Builder authors(Set<String> authors) { this.authors = authors; return this; } public Builder price(BigDecimal price) { this.price = price; return this; } public ProductCatalog build() { return new ProductCatalog(this); } } @Override public String toString() { final StringBuffer sb = new StringBuffer("ProductCatalog{"); sb.append("id=").append(id); sb.append(", title='").append(title).append('\''); sb.append(", isbn='").append(isbn).append('\''); sb.append(", authors=").append(authors); sb.append(", price=").append(price); sb.append('}'); return sb.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ProductCatalog that = (ProductCatalog) o; return id.equals(that.id) && title.equals(that.title) && Objects.equals(isbn, that.isbn) && Objects.equals(authors, that.authors) && Objects.equals(price, that.price); } @Override public int hashCode() { return Objects.hash(id, title, isbn, authors, price); } @Override @DynamoDbIgnore public int compareTo(ProductCatalog other) { if (this.id.compareTo(other.id) != 0){ return this.id.compareTo(other.id); } else { return this.title.compareTo(other.title); } } }
Les deux exemples de code suivants illustrant une écriture par lots illustrent le caractère verbeux et le manque de sécurité de type liés à l'utilisation du client standard par opposition au client amélioré.
public static void batchWriteStandard(DynamoDbClient dynamoDbClient, String tableName) { Map<String, AttributeValue> catalogItem = Map.of( "authors", AttributeValue.builder().ss("a", "b").build(), "id", AttributeValue.builder().n("1").build(), "isbn", AttributeValue.builder().s("1-565-85698").build(), "title", AttributeValue.builder().s("Title 1").build(), "price", AttributeValue.builder().n("52.13").build()); Map<String, AttributeValue> catalogItem2 = Map.of( "authors", AttributeValue.builder().ss("a", "b", "c").build(), "id", AttributeValue.builder().n("2").build(), "isbn", AttributeValue.builder().s("1-208-98073").build(), "title", AttributeValue.builder().s("Title 2").build(), "price", AttributeValue.builder().n("21.99").build()); Map<String, AttributeValue> catalogItem3 = Map.of( "authors", AttributeValue.builder().ss("g", "k", "c").build(), "id", AttributeValue.builder().n("3").build(), "isbn", AttributeValue.builder().s("7-236-98618").build(), "title", AttributeValue.builder().s("Title 3").build(), "price", AttributeValue.builder().n("42.00").build()); Set<WriteRequest> writeRequests = Set.of( WriteRequest.builder().putRequest(b -> b.item(catalogItem)).build(), WriteRequest.builder().putRequest(b -> b.item(catalogItem2)).build(), WriteRequest.builder().putRequest(b -> b.item(catalogItem3)).build()); Map<String, Set<WriteRequest>> productCatalogItems = Map.of( "ProductCatalog", writeRequests); BatchWriteItemResponse response = dynamoDbClient.batchWriteItem(b -> b.requestItems(productCatalogItems)); logger.info("Unprocessed items: " + response.unprocessedItems().size()); }
public static void batchWriteEnhanced(DynamoDbTable<ProductCatalog> productCatalog) { ProductCatalog prod = ProductCatalog.builder() .id(1) .isbn("1-565-85698") .authors(new HashSet<>(Arrays.asList("a", "b"))) .price(BigDecimal.valueOf(52.13)) .title("Title 1") .build(); ProductCatalog prod2 = ProductCatalog.builder() .id(2) .isbn("1-208-98073") .authors(new HashSet<>(Arrays.asList("a", "b", "c"))) .price(BigDecimal.valueOf(21.99)) .title("Title 2") .build(); ProductCatalog prod3 = ProductCatalog.builder() .id(3) .isbn("7-236-98618") .authors(new HashSet<>(Arrays.asList("g", "k", "c"))) .price(BigDecimal.valueOf(42.00)) .title("Title 3") .build(); BatchWriteResult batchWriteResult = DependencyFactory.enhancedClient() .batchWriteItem(b -> b.writeBatches( WriteBatch.builder(ProductCatalog.class) .mappedTableResource(productCatalog) .addPutItem(prod).addPutItem(prod2).addPutItem(prod3) .build() )); logger.info("Unprocessed items: " + batchWriteResult.unprocessedPutItemsForTable(productCatalog).size()); }