Pelajari dasar-dasar DynamoDB Enhanced Client API - AWS SDK for Java 2.x

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Pelajari dasar-dasar DynamoDB Enhanced Client API

Topik ini membahas fitur dasar dari DynamoDB Enhanced API Client dan membandingkannya dengan klien DynamoDB standar. API

Jika Anda baru mengenal DynamoDB Enhanced API Client, kami sarankan Anda melalui tutorial pengantar untuk membiasakan diri dengan kelas-kelas dasar.

item DynamoDB di Java

Tabel DynamoDB menyimpan item. Bergantung pada kasus penggunaan Anda, item di sisi Java dapat berbentuk data atau struktur terstruktur statis yang dibuat secara dinamis.

Jika kasus penggunaan Anda memanggil item dengan kumpulan atribut yang konsisten, gunakan kelas beranotasi atau gunakan pembuat untuk menghasilkan tipe statis yang sesuai. TableSchema

Atau, jika Anda perlu menyimpan item yang terdiri dari berbagai struktur, buatDocumentTableSchema. DocumentTableSchemaadalah bagian dari Enhanced Document API dan hanya memerlukan kunci primer yang diketik secara statis dan bekerja dengan EnhancedDocument instance untuk menyimpan elemen data. Dokumen API yang Ditingkatkan dibahas dalam topik lain.

Jenis atribut untuk kelas model data

Meskipun DynamoDB mendukung sejumlah kecil jenis atribut dibandingkan dengan sistem tipe kaya Java, DynamoDB Enhanced API Client menyediakan mekanisme untuk mengkonversi anggota kelas Java ke dan dari jenis atribut DynamoDB.

Jenis atribut (properti) kelas data Java Anda harus berupa tipe objek, bukan primitif. Misalnya, selalu gunakan Long dan Integer objek tipe data, bukan long dan int primitif.

Secara default, DynamoDB Enhanced API Client mendukung konverter atribut untuk sejumlah besar jenis, seperti Integer, String, dan Instant. BigDecimal Daftar ini muncul di kelas implementasi AttributeConverter antarmuka yang dikenal. Daftar ini mencakup banyak jenis dan koleksi seperti peta, daftar, dan set.

Untuk menyimpan data untuk jenis atribut yang tidak didukung secara default atau tidak sesuai dengan JavaBean konvensi, Anda dapat menulis AttributeConverter implementasi kustom untuk melakukan konversi. Lihat bagian konversi atribut untuk contoh.

Untuk menyimpan data untuk tipe atribut yang kelasnya sesuai dengan spesifikasi kacang Java (atau kelas data yang tidak dapat diubah), Anda dapat mengambil dua pendekatan.

  • Jika Anda memiliki akses ke file sumber, Anda dapat membuat anotasi kelas dengan @DynamoDbBean (atau@DynamoDbImmutable). Bagian yang membahas atribut bersarang menunjukkan contoh penggunaan kelas beranotasi.

  • Jika tidak memiliki akses ke file sumber dari kelas JavaBean data untuk atribut (atau Anda tidak ingin membubuhi keterangan file sumber kelas yang Anda memiliki akses ke), maka Anda dapat menggunakan pendekatan builder. Ini menciptakan skema tabel tanpa mendefinisikan kunci. Kemudian, Anda dapat menyarangkan skema tabel ini di dalam skema tabel lain untuk melakukan pemetaan. Bagian atribut bersarang memiliki contoh yang menunjukkan penggunaan skema bersarang.

Nilai nol

Saat Anda menggunakan putItem metode ini, klien yang disempurnakan tidak menyertakan atribut bernilai nol dari objek data yang dipetakan dalam permintaan ke DynamoDB.

Perilaku default untuk updateItem permintaan menghapus atribut dari item di DynamoDB yang disetel ke null di objek yang Anda kirimkan dalam metode. SDK updateItem Jika Anda bermaksud memperbarui beberapa nilai atribut dan menjaga yang lain tidak berubah, Anda memiliki dua opsi.

  • Ambil item (dengan menggunakangetItem) sebelum Anda membuat perubahan pada nilai. Dengan menggunakan pendekatan ini, SDK mengirimkan semua nilai yang diperbarui dan lama ke DynamoDB.

  • Gunakan salah satu IgnoreNullsMode.SCALAR_ONLY atau IgnoreNullsMode.MAPS_ONLY ketika Anda membuat permintaan untuk memperbarui item. Kedua mode mengabaikan properti bernilai nol dalam objek yang mewakili atribut skalar di DynamoDB. Perbarui item yang berisi tipe kompleksTopik dalam panduan ini berisi informasi lebih lanjut tentang IgnoreNullsMode nilai dan cara bekerja dengan tipe yang kompleks.

Contoh berikut menunjukkan ignoreNullsMode() updateItem() metode ini.

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]

Metode dasar DynamoDB Enhanced Client

Metode dasar peta klien yang disempurnakan ke operasi layanan DynamoDB yang dinamai menurut namanya. Contoh berikut menunjukkan variasi paling sederhana dari setiap metode. Anda dapat menyesuaikan setiap metode dengan meneruskan objek permintaan yang disempurnakan. Objek permintaan yang disempurnakan menawarkan sebagian besar fitur yang tersedia di klien DynamoDB standar. Mereka sepenuhnya didokumentasikan dalam AWS SDK for Java 2.x API Referensi.

Contoh menggunakan yang Customer kelas ditunjukkan sebelumnya.

// 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));

Bandingkan DynamoDB Enhanced Client dengan klien DynamoDB standar

Baik APIs klien DynamoDB — standar dan ditingkatkan — memungkinkan Anda bekerja dengan tabel DynamoDB untuk CRUD melakukan (membuat, membaca, memperbarui, dan menghapus) operasi tingkat data. Perbedaan antara klien APIs adalah bagaimana hal itu dicapai. Menggunakan klien standar, Anda bekerja secara langsung dengan atribut data tingkat rendah. Klien yang disempurnakan API menggunakan kelas dan peta Java yang sudah dikenal ke tingkat rendah API di belakang layar.

Sementara kedua klien APIs mendukung operasi tingkat data, klien DynamoDB standar juga mendukung operasi tingkat sumber daya. Operasi tingkat sumber daya mengelola database, seperti membuat cadangan, daftar tabel, dan memperbarui tabel. Klien yang disempurnakan API mendukung sejumlah operasi tingkat sumber daya tertentu seperti membuat, mendeskripsikan, dan menghapus tabel.

Untuk mengilustrasikan pendekatan berbeda yang digunakan oleh dua klienAPIs, contoh kode berikut menunjukkan pembuatan ProductCatalog tabel yang sama menggunakan klien standar dan klien yang disempurnakan.

Bandingkan: Buat tabel menggunakan klien DynamoDB standar

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)) );

Bandingkan: Buat tabel menggunakan DynamoDB Enhanced Client

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)) ) );

Klien yang disempurnakan menggunakan kelas data beranotasi berikut. DynamoDB Enhanced Client memetakan tipe data Java ke tipe data DynamoDB untuk kode verbose yang lebih sedikit yang lebih mudah diikuti. ProductCatalogadalah contoh menggunakan kelas yang tidak dapat diubah dengan DynamoDB Enhanced Client. Penggunaan kelas Immutable untuk kelas data yang dipetakan dibahas nanti dalam topik ini.

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); } } }

Dua contoh kode berikut dari penulisan batch menggambarkan verboseness dan kurangnya keamanan tipe saat menggunakan klien standar sebagai lawan dari klien yang ditingkatkan.

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()); }