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 DynamoDB Enhanced Client API dan membandingkannya dengan API klien DynamoDB standar.

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

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. Enhanced Document API dibahas dalam topik lain.

Tipe atribut untuk kelas model data

Meskipun DynamoDB mendukung sejumlah kecil tipe atribut dibandingkan dengan sistem tipe kaya Java, DynamoDB Enhanced Client API menyediakan mekanisme untuk mengonversi anggota kelas Java ke dan dari tipe 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 Client API mendukung konverter atribut untuk sejumlah besar tipe, 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 khusus 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 API, klien yang disempurnakan tidak menyertakan atribut bernilai nol dari objek data yang dipetakan dalam permintaan ke DynamoDB.

Untuk updateItem permintaan, atribut bernilai nol dihapus dari item pada database. Jika Anda bermaksud memperbarui beberapa nilai atribut dan menjaga yang lain tidak berubah, salin nilai atribut lain yang tidak boleh diubah atau gunakan metode ignoreNull () pada pembuat pembaruan.

Contoh berikut menunjukkan ignoreNulls() the updateItem() metode.

public void updateItemNullsExample(){ Customer customer = new Customer(); customer.setCustName("CustName"); customer.setEmail("email"); customer.setId("1"); customer.setRegistrationDate(Instant.now()); // Put item with values for all attributes. customerDynamoDbTable.putItem(customer); // Create a Customer instance with the same id value, but a different name value. // Do not set the 'registrationDate' attribute. Customer custForUpdate = new Customer(); custForUpdate.setCustName("NewName"); custForUpdate.setEmail("email"); custForUpdate.setId("1"); // Update item without setting the registrationDate attribute. customerDynamoDbTable.updateItem(b -> b .item(custForUpdate) .ignoreNulls(Boolean.TRUE)); Customer updatedWithNullsIgnored = customerDynamoDbTable.getItem(customer); // registrationDate value is unchanged. logger.info(updatedWithNullsIgnored.toString()); customerDynamoDbTable.updateItem(custForUpdate); Customer updatedWithNulls = customerDynamoDbTable.getItem(customer); // registrationDate value is null because ignoreNulls() was not used. logger.info(updatedWithNulls.toString()); } } // Logged lines. Customer [id=1, custName=NewName, email=email, registrationDate=2023-04-05T16:32:32.056Z] Customer [id=1, custName=NewName, email=email, registrationDate=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 Referensi AWS SDK for Java 2.x API.

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 API klien DynamoDB — standar dan yang disempurnakan —memungkinkan Anda bekerja dengan tabel DynamoDB untuk melakukan operasi tingkat data CRUD (membuat, membaca, memperbarui, dan menghapus). Perbedaan antara API klien adalah bagaimana hal itu dicapai. Menggunakan klien standar, Anda bekerja secara langsung dengan atribut data tingkat rendah. API klien yang disempurnakan menggunakan kelas Java yang sudah dikenal dan memetakan ke API tingkat rendah di belakang layar.

Sementara kedua API klien 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. API klien yang disempurnakan mendukung sejumlah operasi tingkat sumber daya tertentu seperti membuat, mendeskripsikan, dan menghapus tabel.

Untuk mengilustrasikan pendekatan berbeda yang digunakan oleh dua API klien, 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()); }