Reputation: 291
We have a dynamoDb table, and one column name "createdAt" is created sometimes as S (String) data type and sometimes with N (Number) data type.
In my code if i define as String, it fails when i want to fetch data and it is number:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@EqualsAndHashCode
@DynamoDBTable(tableName = "SomeTable")
public class SomeTable {
@DynamoDBAttribute
@DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.S)
private Long createdAt;
}
and if i define as Number , it fails when i want to fetch data and it is string in table:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@EqualsAndHashCode
@DynamoDBTable(tableName = "SomeTable")
public class SomeTable {
@DynamoDBAttribute
@DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.N)
private Long createdAt;
}
do any of you had the same issue before maybe? there should be a way to fix it right? and it is not option to have only one datatype :(
Upvotes: 2
Views: 1719
Reputation: 163
I had the same issue. I acknowledge it is a sign of something very wrong and there were several things that lead to our situation, but we ended up with two processes that were writing the to the same table. The new process wrote longs for the dateModified
field while the old process wrote a timestamp string.
In order to do get the job done, I wrote a custom converter:
import java.time.Instant;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
public class StringOrLongConverter implements DynamoDBTypeConverter<AttributeValue, Long> {
@Override
public AttributeValue convert(Long timeInMilli) { // convert from Java Long to AttributeValue (Number) to save in Dynamo
try {
return new AttributeValue().withN(String.valueOf(timeInMilli));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Long unconvert(AttributeValue av) {
try {
return Long.valueOf(av.getN()); // try to parse as a number
} catch (NumberFormatException e) {
String dateString = av.getS(); // try to parse as a timestamp
Instant instant = Instant.parse(dateString);
return instant.toEpochMilli();
}
}
}
and then I just utilized that converter in my model:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverted;
import lombok.Data;
@Data
@DynamoDBTable(tableName = "my-table")
public class User {
@DynamoDBHashKey(attributeName = "id")
private String id;
@DynamoDBTypeConverted(converter = StringOrLongConverter.class) // utilize the converter here
@DynamoDBAttribute(attributeName = "dateModified")
private Long dateModified;
}
There is probably a more elegant way to check the type of the AttributeValue
of the field in question other than a try...catch
but it got the job done for me.
Upvotes: 0
Reputation: 3697
Saving the same attribute with different types sounds like a bad design if you can use two attributes.
However, if you absolutely need to store it like that you can use a custom converter.
@DynamoDBAttribute
@DynamoDBTypeConverted(converter = CustomConverter.class)
private Long createdAt;
and then the converter
class CustomConverter<Object> implements AttributeConverter<Object> {
... implement the methods here handling both cases String and Long
Upvotes: 2