Monthly Archives: December 2021

DynamodDb access in Java(Code)

Assume we have below data in DynamoDb. All fields are String type. PK is ID, SK is name.

ddb_data

We use DynamoDbEnhancedAsyncClient to access DynamoDb data.

To get a specific record with (PK, SK):

public static void testGetItem() throws Exception {
    CompletableFuture<Record> recordFuture = table.getItem(Key.builder()
            .partitionValue("001")
            .sortValue(33)
            .build());
    System.out.println(recordFuture.get());
}

To query only on PK:

public static void testQueryNormal() throws Exception {
    QueryConditional queryConditional = QueryConditional.keyEqualTo(Key.builder()
            .partitionValue("003")
            .build());
    PagePublisher<Record> pagePublisher = table.query(queryConditional);
    Flux<Page<Record>> recordFlux = Flux.from(pagePublisher);
    recordFlux.subscribe(record -> {
        System.out.println(record.items());
    });
    Thread.sleep(2000l);
}

To query on (PK, SK prefix):

public static void testQuerySomeSortKey() throws Exception {
    QueryConditional queryConditional = QueryConditional.sortBeginsWith(Key.builder()
            .partitionValue("002")
            .sortValue("pp")
            .build());
    PagePublisher<Record> pagePublisher = table.query(queryConditional);
    Flux<Page<Record>> recordFlux = Flux.from(pagePublisher);
    recordFlux.subscribe(record -> {
        System.out.println(record.items());
        System.out.println(record.lastEvaluatedKey());
    });
    Thread.sleep(2000l);
}

To query with pagination:

public Mono<RecordWithLastKey> queryPagination1stCall() throws Exception {
    QueryConditional queryConditional = QueryConditional.keyEqualTo(Key.builder()
            .partitionValue("003")
            .build());
    QueryEnhancedRequest enhancedRequest = QueryEnhancedRequest.builder()
            .queryConditional(queryConditional)
            .limit(2)
            .build();
    PagePublisher<Record> pagePublisher = table.query(enhancedRequest);
    Flux<Page<Record>> recordFlux = Flux.from(pagePublisher);

    return recordFlux.next()
            .map(record -> new RecordWithLastKey(record.items(), getRecordKey(record.lastEvaluatedKey())));
}

public Mono<RecordWithLastKey> queryPaginationNextCall(String pk, String sk) throws Exception {
    QueryConditional queryConditional = QueryConditional.keyEqualTo(Key.builder()
            .partitionValue("003")
            .build());
    Map<String, AttributeValue> lastEvaluatedKey = new HashMap<>();
    lastEvaluatedKey.put("ID", AttributeValue.builder().s(pk).build());
    lastEvaluatedKey.put("NAME", AttributeValue.builder().s(sk).build());
    QueryEnhancedRequest enhancedRequest = QueryEnhancedRequest.builder()
            .queryConditional(queryConditional)
            .exclusiveStartKey(lastEvaluatedKey)
            .limit(2)
            .build();

    PagePublisher<Record> pagePublisher = table.query(enhancedRequest);
    Flux<Page<Record>> recordFlux = Flux.from(pagePublisher);

    return recordFlux.next()
            .map(record -> new RecordWithLastKey(record.items(), getRecordKey(record.lastEvaluatedKey())));
}

private RecordKey getRecordKey(Map<String, AttributeValue> valueMap) {
    RecordKey recordKey = null;
    if (valueMap != null) {
        recordKey = RecordKey.builder()
                .pk(valueMap.get("ID").s())
                .sk(valueMap.get("NAME").s())
                .build();
    }
    return recordKey;
}

record1

record2

record3

see code on github.

DynamoDB access in Java(structure)

dynamodb_hierarchy

1. dynamodb vs dynamodb-enhanced
1). software.amazon.awssdk, dynamodb
2). software.amazon.awssdk, dynamodb-enhanced, a high-level library. Can map class to DynamoDb tables.
Introducing enhanced DynamoDB client in the AWS SDK for Java v2

2. Client vs Table
DynamoDbClient/DynamoDbEnhancedClient is like databases access. Think you know the username/password to the database.
DynamoDbTable/DynamoDbAsyncTable is the table you will operate on.

AsyncClient could return CompletableFuture or Publisher(in reactive)

Below are some junk demo code.

// dynamodb
DynamoDbAsyncClient dynamoDbAsyncClient = null;
QueryRequest queryRequest = null;
Flux.from(dynamoDbAsyncClient.queryPaginator(queryRequest))
        .subscribe();

// dynamodb enhanced
// sync
DynamoDbEnhancedClient dynamoDbClient = null;
DynamoDbTable dynamoDbTable = dynamoDbClient.table("tableName", TableSchema.class);
PageIterable pageIterable = dynamoDbTable.query(SOME_QUERY); // return

// async
DynamoDbEnhancedAsyncClient dynamoDbEnhancedAsyncClient1 = null;
DynamoDbAsyncTable dynamoDbAsyncTable = dynamoDbEnhancedAsyncClient1.table(null, null);
PagePublisher<T> pagePublisher = dynamoDbAsyncTable.query(SOME_QUERY);
Flux.from(pagePublisher).subscribe(p -> {
    System.out.println();
});

dependencyManagement as jar

Without specifying <packaging>xxx</packaging> in pom, the default is jar.

For project1, it is deployed to nexus. Then project2 can uses it as normal dependency rather than parent. project2 can still uses the version, scope inside dependencyManagement in project1. See below.

project1/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.test.pengli</groupId>
    <artifactId>project1</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>sts</artifactId>
                <version>2.17.74</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.6</version>
        </dependency>
    </dependencies>

</project>

 

project2/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.test.pengli</groupId>
    <artifactId>project2</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>sts</artifactId>
                <version>2.17.74</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>com.test.pengli</groupId>
            <artifactId>project1</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>sts</artifactId>
        </dependency>
    </dependencies>

    <pluginRepositories>
        <pluginRepository>
            <id>xxx</id>
            <name>Nexus</name>
            <url>http://nexus.xxx.com</url>
        </pluginRepository>
    </pluginRepositories>
</project>

When running mvn dependency:tree, it shows below. It actually same as result in dependencyManagement vs dependencies

dependency_tree2

 

dependencyManagement vs dependencies

dependencies uses the dependencies.

dependencyManagement doesn’t include dependencies in project. It is mainly defines dependencies and its version, scope. Sub project specifies parent to it. When sub project defines dependencies, it won’t need to specify version, scope.

pro1_2

project1/pom.xml

This pom.xml has <packaging>pom</packaging> property. In this case, it can be used as parent in sub project.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.test.pengli</groupId>
    <artifactId>project1</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging> // This is needed if we want this pom.xml to be a parent

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>sts</artifactId>
                <version>2.17.74</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.6</version>
        </dependency>
    </dependencies>

</project>

project2/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.test.pengli</groupId>
        <artifactId>project1</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../project1</relativePath>
    </parent>

    <groupId>com.test.pengli</groupId>
    <artifactId>project2</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>sts</artifactId>  // No need to specify version. Parent pom did for us.
        </dependency>
    </dependencies>

</project>

In project2, when run mvn dependency:tree, it shows below:

dependency_tree