One more option. If you want to reduce code boilerplates you might use Lombok's @Getter this way:

  @Getter(onMethod = @__({@DynamoDbAttribute("address")}))
  private String address;

p.s.: It won't impact performance cause the code generation happens not in runtime but the entity looks better IMO.

Answer from Alex A on Stack Overflow
🌐
AWS
docs.aws.amazon.com › AWSJavaSDK › latest › javadoc › com › amazonaws › services › dynamodbv2 › datamodeling › DynamoDBAttribute.html
DynamoDBAttribute (AWS SDK for Java - 1.12.797)
Interface for marking a class property as an attribute in a DynamoDB table. Applied to the getter method or the class field for a modeled property. If the annotation is applied directly to the class field, the corresponding getter and setter must be declared in the same class.
Discussions

java - @DynamoDBAttribute annotation not working - Stack Overflow
I have a dynamodb table named opx_user_profiles. The entity is shown below, however the attribute user_profile_id is getting saved as userProfileID in the table, even though the @DynamoDBAttribute( More on stackoverflow.com
🌐 stackoverflow.com
amazon web services - DynamoDB Mapper annotation for Object which has list of another object - Stack Overflow
I just added @DynamoDBTypeConvertedJson ... @DynamoDBAttribute(attributeName = “EmployeeLevelDataRecords”) public EmployeeLevelTrail employeeLevelTrail 2019-03-04T22:55:23.783Z+00:00 ... Haha, I forgot about @DynamoDBTypeConvertedJson when I was writing my answer. If it works for you, make an answer explaining it and accept it so that others can see how you solved it. 2019-03-05T01:36:38.073Z+00:00 ... The @DynamoDBTypeConvertedJson annotation does not help ... More on stackoverflow.com
🌐 stackoverflow.com
Enhanced DynamoDB annotations are incompatible with Lombok
Describe the Feature Lombok is a popular system for reducing boilerplate code, especially in data-centric objects such as database entries. By annotating the class and/or member variables, it autog... More on github.com
🌐 github.com
26
June 30, 2020
DynamoDB Mapper attributeName not honored on member variable with identical name
This works fine if I put the DynamoDBAttribute annotation on the getter, but not if I put the exact same annotation on the member variable itself instead. If I do put the annotation on the member variable, I must rename the member variable and getter / setter to something else, at which point the attributeName ... More on github.com
🌐 github.com
2
January 7, 2022
🌐
AWS
awslabs.github.io › dynamodb-data-mapper-js › packages › dynamodb-data-mapper-annotations
@aws/dynamodb-data-mapper-annotations
Marks a property as an attribute in the mapped DynamoDB record. The annotation will attempt to infer the correct DynamoDB type from the TypeScript type metadata, though no metadata will be available for any generic type parameters used.
🌐
AWS
sdk.amazonaws.com › java › api › latest › software › amazon › awssdk › enhanced › dynamodb › mapper › annotations › package-summary.html
software.amazon.awssdk.enhanced.dynamodb.mapper.annotations (AWS SDK for Java - 2.42.36)
This annotation is used to flatten attributes into top-level attributes of the record that is read and written to the database. ... Opts this attribute out of participating in the table schema. ... Specifies that when calling TableSchema.itemToMap(Object, boolean), a separate DynamoDB object ...
Top answer
1 of 3
11

To mark another class as part of the data model for DynamoDBMapper, you can annotate it with @DynamoDBDocument, which tells DynamoDBMapper that a class can be serialized as a DynamoDB document.

For classes that you aren't writing (ie. from the Java library or an external library) or if you need more control over how to serialize one of your own classes, you can use @DynamoDBTypeConverted, which allows you to map arbitrary data by providing your own DynamoDBTypeConverter implementation to convert from any Java object to any supported DynamoDB type.

Using your sample code, I've added in the appropriate @DynamoDBDocument and @DynamoDBTypeConverted annotations, as well as a sample implementation of a DynamoDBTypeConverter that converts an Instant to a ISO-8601 String. If employeeId is the hash key of your table, make sure you also add the @DynamoDBHashKey annotation to employeeId.

@DynamoDBTable(tableName = TABLE_NAME)
public class EmployeeData {
    public final static String TABLE_NAME = “EmployeeDataRecord”;

    @DynamoDBAttribute(attributeName = “employeeID”)
    public String EmployeeID;

    @DynamoDBAttribute(attributeName = “EmployeeLevelDataRecords”)
    public EmployeeLevelTrail employeeLevelTrail

}

@DynamoDBDocument
public class EmployeeLevelTrail {

    public final static String DDB_ATTR_EMPLOYEE_LEVEL_TRAIL = “employeeLevelTrail”;

    @DynamoDBAttribute(attributeName = DDB_ATTR_EMPLOYEE_LEVEL_TRAIL)
    private List<EmployeeLevelRecord> thisEmployeeLevelRecords;

    public void appendEmployeeLevelRecord(@NonNull EmployeeLevelRecord employeeLevelRecord) {

        thisEmployeeLevelRecords.add(employeeLevelRecord);

    }
}

@DynamoDBDocument
public class EmployeeLevelRecord {

    private String Level;

    private String Manager;

    @DynamoDBTypeConverted(converter = InstantToStringTypeConverter.class)
    private Instant timeOfEvent;

}

public class InstantToStringTypeConverter implements DynamoDBTypeConverter<String, Instant> {

    @Override
    public String convert(final Instant instant) {
        return instant.toString();
    }

    @Override
    public Instant unconvert(final String string) {
        return Instant.parse(string);
    }
}
2 of 3
2

I reached to the solution using the below code. With only @DynamoDBTypeConvertedJSON, I was able to create an entry in the table but not update it as I was running into mapping Dynamo DB mapping exception.

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@DynamoDBTable(tableName = TABLE_NAME)
public class EmployeeData {

    public final static String TABLE_NAME = "EmployeeData";

    public final static String DDB_ATTR_ID = "Id";

    public final static String DDB_ATTR_EMPLOYEE_LEVEL_RECORD_TRAIL = "EmployeeLevelRecordTrail";

    @DynamoDBHashKey(attributeName = DDB_ATTR_ID)
    @DynamoDBAttribute(attributeName = DDB_ATTR_ID)
    private String id;

    @DynamoDBAttribute(attributeName = DDB_ATTR_EMPLOYEE_LEVEL_RECORD_TRAIL)
    @DynamoDBTypeConverted(converter = EmployeeLevelRecordTrailConverter.class)
    private  EmployeeLevelRecordTrail  employeeLevelRecordTrail;

    @DynamoDBAttribute(attributeName = DDB_ATTR_CREATED_TIME)
    @DynamoDBTypeConverted(converter = InstantConverter.class)
    private Instant joiningTime;
}



@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class EmployeeLevelRecordTrail {

    private List<EmployeeLevelRecord> thisEmployeeLevelRecords;

    public void appendEmployeeLevelRecord(@NonNull EmployeeLevelRecord employeeLevelRecord) {

        thisEmployeeLevelRecords.add(employeeLevelRecord);

    }
}


@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@DynamoDBDocument
public class EmployeeLevelRecord {

    private String Level;

    private String Manager;

    @DynamoDBTypeConverted(converter = InstantConverter.class)
    private Instant timeOfEvent;

}

public class EmployeeLevelRecordTrailConverter implements
        DynamoDBTypeConverter<List<EmployeeLevelRecord>, EmployeeLevelRecordTrail> {

    @Override
    public List<EmployeeLevelRecord> convert(EmployeeLevelRecordTrail employeeLevelRecordTrail) {
        return employeeLevelRecordTrail.getEmployeeLevelRecord();
    }

    @Override
    public EmployeeLevelRecordTrail unconvert(List<EmployeeLevelRecord> thisEmployeeLevelRecords) {
        return new EmployeeLevelRecordTrail(thisEmployeeLevelRecords);
    }
}

public class InstantConverter implements DynamoDBTypeConverter<String, Instant> {

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ISO_INSTANT;

    @Override
    public String convert(Instant instant) {
        return instant == null ? null : DATE_TIME_FORMATTER.format(instant);
    }

    @Override
    public Instant unconvert(String str) {
        return str == null ? null : Instant.from(DATE_TIME_FORMATTER.parse(str));
    }
}

Reading the documentation on how to do mapping helped.

Find elsewhere
🌐
GitHub
github.com › awslabs › dynamodb-data-mapper-js › tree › master › packages › dynamodb-data-mapper-annotations
dynamodb-data-mapper-js/packages/dynamodb-data-mapper-annotations at master · awslabs/dynamodb-data-mapper-js
Marks a property as an attribute in the mapped DynamoDB record. The annotation will attempt to infer the correct DynamoDB type from the TypeScript type metadata, though no metadata will be available for any generic type parameters used.
Author   awslabs
🌐
AWS
docs.aws.amazon.com › aws sdk for java › developer guide for version 2.x › calling aws services from the aws sdk for java 2.x › work with dynamodb › map java objects to dynamodb items with the aws sdk for java 2.x › data class annotations
Data class annotations - AWS SDK for Java 2.x
This guide shows annotations on getters. 2The term property is normally used for a value encapsulated in a JavaBean data class. However, this guide uses the term attribute instead, to be consistent with the terminology used by DynamoDB.
🌐
AWS
docs.aws.amazon.com › amazon dynamodb › developer guide › programming with dynamodb and the aws sdks › overview of aws sdk support for dynamodb › higher-level programming interfaces for dynamodb › java 1.x: dynamodbmapper
Java 1.x: DynamoDBMapper - Amazon DynamoDB
You can map a class in your client application to the ProductCatalog table as shown in the following Java code. This code defines a plain old Java object (POJO) named CatalogItem, which uses annotations to map object fields to DynamoDB attribute names.
🌐
npm
npmjs.com › package › @aws › dynamodb-data-mapper-annotations
@aws/dynamodb-data-mapper-annotations - npm
Marks a property as an attribute in the mapped DynamoDB record. The annotation will attempt to infer the correct DynamoDB type from the TypeScript type metadata, though no metadata will be available for any generic type parameters used.
      » npm install @aws/dynamodb-data-mapper-annotations
    
Published   Aug 15, 2018
Version   0.7.3
Author   AWS SDK for JavaScript Team
🌐
GitHub
github.com › aws › aws-sdk-java-v2 › issues › 1932
Enhanced DynamoDB annotations are incompatible with Lombok · Issue #1932 · aws/aws-sdk-java-v2
June 30, 2020 - This creates problems with enhanced dynamodb, because the annotations such as DynamoDbAttribute and DynamoDbPartitionKey cannot be applied to member variables, only to setter/getter functions, and the setter/getter function is not present in the source code. In DynamoDbMapper (from AWS SDK v1), annotations could be applied to functions or member variables, so Lombok worked well.
Author   aws
🌐
GitHub
github.com › aws › aws-sdk-java › issues › 2695
DynamoDB Mapper attributeName not honored on member variable with identical name · Issue #2695 · aws/aws-sdk-java
January 7, 2022 - Describe the bug When the DynamoDBAttribute annotation is applied to a member variable rather than the corresponding getter, the attributeName parameter is ignored if it is identical to the member variable name. While this may not initia...
Author   aws
🌐
John Spong
jspong.github.io › 2021 › 07 › 19 › AbstractDynamoAttributes.html
Storing abstract and generic attributes in DynamoDB | John Spong
July 19, 2021 - It’s extremely simple to store an object with primitive attributes in DynamoDB: annotate your class with @DynamoDBTable, your fields with @DynamoDBHashKey/@DynamoDBAttribute, and let DynamoDBMapper handle the rest:
🌐
Javadoc.io
javadoc.io › static › com.amazonaws › aws-java-sdk-dynamodb › 1.11.642 › com › amazonaws › services › dynamodbv2 › datamodeling › DynamoDBMapper.html
DynamoDBMapper (AWS Java SDK for Amazon DynamoDB 1.11.642 API)
The table to query is determined by looking at the annotations on the specified class, which declares where to store the object data in Amazon DynamoDB, and the query expression parameter allows the caller to filter results and control how the query is executed. When the query is on any local/global secondary index, callers should be aware that the returned object(s) will only contain item attributes ...
🌐
GitHub
github.com › awslabs › dynamodb-data-mapper-js › blob › master › packages › dynamodb-data-mapper-annotations › src › attribute.ts
dynamodb-data-mapper-js/packages/dynamodb-data-mapper-annotations/src/attribute.ts at master · awslabs/dynamodb-data-mapper-js
November 20, 2018 - import { PropertyAnnotation } from './annotationShapes'; import { METADATA_TYPE_KEY } from './constants'; import { BinarySet, NumberValueSet } from "@aws/dynamodb-auto-marshaller"; import { DynamoDbSchema } from '@aws/dynamodb-data-mapper'; import { DocumentType, KeyableType, Schema, SchemaType, SetType ·
Author   awslabs
🌐
AWS
docs.aws.amazon.com › java › api › latest › software › amazon › awssdk › enhanced › dynamodb › mapper › annotations › DynamoDbBean.html
DynamoDbBean (AWS SDK for Java - 2.43.2)
Class level annotation that identifies this class as being a DynamoDb mappable entity. Any class used to initialize a BeanTableSchema must have this annotation. If a class is used as an attribute type within another annotated DynamoDb class, either as a document or flattened with the ...