DynamoDB PutItem di Java (AWS SDK v2)

PutItem menulis satu item utuh dan mengganti item mana pun yang punya primary key sama (aksi berbasis item membahas bedanya dengan UpdateItem). Di AWS SDK for Java 2.x setiap atribut masuk ke PutItemRequest sebagai AttributeValue bertipe, dan builder-nya akan membiarkan Anda menyusun satu yang mustahil valid.

Kode

import java.util.HashMap;
import java.util.Map;

import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;

public class PutItemExample {
    public static void main(String[] args) {
        try (DynamoDbClient ddb = DynamoDbClient.builder()
                .region(Region.US_EAST_1)
                .build()) {

            Map<String, AttributeValue> item = new HashMap<>();
            item.put("Artist", AttributeValue.builder().s("Arturo Sandoval").build());
            item.put("SongTitle", AttributeValue.builder().s("Cubano Chant").build());
            item.put("AlbumTitle", AttributeValue.builder().s("Danzon").build());
            item.put("Year", AttributeValue.builder().n("1994").build());
            item.put("Awards", AttributeValue.builder().n("0").build());

            Map<String, String> names = new HashMap<>();
            names.put("#cond0", "Artist");
            names.put("#cond1", "SongTitle");

            PutItemRequest request = PutItemRequest.builder()
                    .tableName("Music")
                    .item(item)
                    .conditionExpression("attribute_not_exists(#cond0) AND attribute_not_exists(#cond1)")
                    .expressionAttributeNames(names)
                    .build();

            try {
                ddb.putItem(request);
                System.out.println("Song written");
            } catch (ConditionalCheckFailedException e) {
                System.out.println("A song with that key already exists — not overwritten");
            }
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
        }
    }
}

Penjelasan

AttributeValue.builder().build() bisa dikompilasi. Ia juga tak bisa dikirim. Builder-nya tidak punya field wajib, jadi sebuah atribut yang lupa Anda beri .s(...) lolos pemeriksaan tipe dengan sempurna dan gagal di layanan:

DynamoDbException | ValidationException | Supplied AttributeValue is empty, must contain exactly one of the supported datatypes

Ini adalah bentuk khas-Java dari kesalahan yang dibuat mustahil oleh SDK lain: types.AttributeValueMember* milik Go adalah tipe-tipe terpisah, jadi tidak ada apa pun yang bisa dibiarkan kosong. Lihat "Supplied AttributeValue is empty" untuk perbaikan yang lebih luas.

null Java yang diserahkan ke .s(...) tidak menjadi NULL DynamoDB. Versi inilah yang benar-benar menggigit, karena ia tampak seperti sebuah nilai:

AttributeValue.builder().s(customer.getNotes()).build()   // getNotes() returned null

Tidak ada NullPointerException yang dilempar saat konstruksi. Builder-nya sekadar tidak mencatat apa-apa, dan permintaannya gagal dengan pesan Supplied AttributeValue is empty yang identik, menunjuk ke atribut yang tak pernah Anda curigai. Kalau Anda memang mau null sungguhan, itu AttributeValue.builder().nul(true).build(); lebih sering yang Anda mau adalah menghilangkan entri itu. Perhatikan bahwa ini kebalikan dari SDK Go, di mana pointer nil di-marshal menjadi NULL dan diam-diam menciptakan sebuah atribut; keduanya dijalankan terhadap engine yang sama pada hari yang sama.

getMessage() bukan pesan layanan. SDK menambahkan konteksnya sendiri, jadi string-nya adalah:

The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 77a08ef1-a3a9-4f97-b309-4cb0741edd1a) (SDK Attempt Count: 1)

Cocokkan pada e.awsErrorDetails().errorCode() dan baca e.awsErrorDetails().errorMessage() untuk teks polosnya. Apa pun yang membandingkan getMessage() dengan sebuah literal akan rusak oleh percobaan ulang, yang mengubah jumlah percobaannya.

Tangkap ConditionalCheckFailedException sebelum DynamoDbException, dan baca apa yang dibawanya. Ia turunan DynamoDbException, jadi mengurutkan blok catch secara terbalik membuat handler yang spesifik tak pernah tercapai. Pada objek yang tertangkap: statusCode() mengembalikan 400 dan retryable() mengembalikan false, yang merupakan jawaban jujur untuk penolakan logika bisnis. Tambahkan .returnValuesOnConditionCheckFailure("ALL_OLD") pada permintaannya dan e.item() kembali terisi dengan item yang memblokir penulisan itu (lima atribut pada eksekusi di atas, Year sebagai AttributeValue(N=1994)), jadi Anda tak perlu getItem susulan untuk mengetahui siapa yang menang.

Angka masuk sebagai string lewat .n(...). Tipe N DynamoDB adalah teks desimal di wire, dan itulah yang menjaga 1994 tidak berubah jadi double. .n(String.valueOf(year)) adalah idiomnya; tidak ada overload .n(int) yang bisa Anda raih.

Client-nya Closeable, dan berumur panjang. try-with-resources di atas tepat untuk program sekali jalan dan keliru untuk sebuah layanan: DynamoDbClient memiliki connection pool HTTP dan bersifat thread-safe, jadi bangun satu per aplikasi dan biarkan ia hidup. Membangun satu per permintaan adalah bug performa Java paling umum pada API ini.

Lebih suka bean daripada map AttributeValue? DynamoDB Enhanced Client (software.amazon.awssdk.enhanced.dynamodb) memetakan kelas ber-anotasi langsung menjadi sebuah item, yang menghapus jebakan builder-kosong itu sepenuhnya. Harganya adalah pemindaian reflektif TableSchema.fromBean saat startup, yang bisa dihindari StaticTableSchema kalau itu penting bagi Anda.

Lakukan secara visual

Setiap kegagalan di atas berawal dari nilai bertipe yang dirakit tangan. Konverter DynamoDB JSON gratis menerima JSON biasa dan mengembalikan bentuk bertipenya, jadi Anda bisa melihat persis seperti apa item itu seharusnya di wire sebelum menulis satu pun AttributeValue.builder().

Untuk menulis dan menyunting item terhadap tabel Anda sendiri — satu formulir per atribut, pemilih tipe, salin hasilnya kembali sebagai Java — unduh DynoTable.

Contoh terkait

Referensi

Direproduksi 2026-07-28 dengan AWS SDK for Java 2.49.4 pada OpenJDK 26.0.1, terhadap DynamoDB Local (amazon/dynamodb-local) di port 9000. Teks exception, kode status, dan isi item di atas adalah keluaran yang ditangkap, disalin apa adanya.

Bekerja dengan DynamoDB tanpa Console

Klien desktop DynamoDB yang cepat dan menjalankan SQL sungguhan yang tidak bisa dijalankan DynamoDB — JOINs, GROUP BY, agregasi — dengan editing visual dan agen AI pada kunci Bedrock milik Anda sendiri.

Uji coba gratis 30 hari, tanpa kartu kredit — lalu paket Free tanpa batas waktu.