A Collection of DynamoDB Gotchas in Kotlin

Photo by Arnold Francisca on Unsplash

While working with DynamoDB recently, I encountered a few tricky cases that I had never had to worry about when working with simple relational databases.

Persisting a DynamoDb Object that extends a class

DynamoDB doesn’t automatically work with Abstract classes. For example, let’s say you have an abstract class which contains some common fields (such as the hashKey and the range fields),

abstract class AbstractBook @JvmOverloads constructor(
@DynamoDBHashKey(attributeName = "isbn")
var isbn: String? = null,

@DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.S)
@DynamoDBRangeKey(attributeName = "author")
var bookType: String? = null
)

And then you define the actual class, that extends this base class,

class Novella @JvmOverloads constructor(
isbn: String? = null,
bookType: String? = null,
var content: String? = null
) : AbstractBook (isbn, bookType)

When you try to use a DynamoDBMapper to save the object it will throw an exception.

To fix the exception, you’ll need to add the @DynamoDBDocument to the AbstractClass to let the mapper know that it's the abstract version of the actual persisted entity. This is what the JavaDoc for the annotation says:

An annotation that marks a class which can be serialised to a DynamoDB document or sub-document. Behaves exactly the same as DynamoDBTable, but without requiring you to specify a tableName.

This means that this annotation is necessary to serialise objects types that are not directly part of the actually stored object type.

Persisting a List of Objects in DynamoDB

Another little snowflake behaviour I encountered was when persisting a list of objects. Let’s say we go ahead and add some more data to the Novella object, like a List of Publishers.

data class Publishers(
val name: String,
val address: String?
)
class Novella @JvmOverloads constructor(
isbn: String? = null,
bookType: String? = null,
var content: String? = null,
@DynamoDBTypeConvertedJson
val publishers: List<Publishers>? = null
) : AbstractBook (isbn, bookType)

@DynamoDBTypeConvertedJson is the annotation DynamoDB recommends for storing objects when using DynamoDBMapper. It has a strange behaviour where it can auto serialise a List<T> but it loses type information on deserialisation and deserialises the object as an amorphous map. This means you get exceptions like: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to Publisher.

Based on this thread this has to do with type erasure, wherein T : Object which results in bad behaviour at deserialisation time (the default Jackson marshaller is smart enough during serialisation).

The best way to solve this is to define a custom serialiser and deserialiser for your object.

So, in this case, it’s a matter of defining a class like,

class PublishersMapListConverter : DynamoDBTypeConverter<String, List<Publishers>> {
private val objectMapper = jacksonObjectMapper()

override fun convert(publishers: List<Publishers>): String {
return objectMapper.writeValueAsString(publishers)
}

override fun unconvert(publishers: String): List<Publishers> {
val type = object : TypeReference<List<Publishers>>() {}
return objectMapper.readValue(publishers, type)
}
}

And then adding the @DynamoDBTypeConverted(converter = PublishersMapListConverter::class),

class Novella @JvmOverloads constructor(
isbn: String? = null,
bookType: String? = null,
var content: String? = null,
@DynamoDBTypeConverted(converter = PublishersMapListConverter::class)
val publishers: List<Publishers>? = null
) : AbstractBook(isbn, bookType)

Hopefully, this post helps someone else save some time as well.

--

--

--

Sr. Software Engineer @ Atlassian. Productivity apps connoisseur. View are my own.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Create a setup so that you can ping Google but not able to ping Facebook from the same system

Amazon VPC CNI vs Calico CNI vs Weave Net CNI on EKS

Code igniter 4 left virtual host hanging

Building an Exchange Rate Helper on Laravel based on Fixer.io API

Ionic Q1 2022 Update Recap

Ionic Q1 2022 Update Splash Screen

Terraform Migrate

0Daily progress: The home stretch! Learning about the Unity profiler.

🤠👊Creating a Flutter App for Chuck in 10 minutes

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Shiveen Pandita

Shiveen Pandita

Sr. Software Engineer @ Atlassian. Productivity apps connoisseur. View are my own.

More from Medium

OAuth in Compose for Desktop with Auth0

Object equality in Java and Kotlin

How we handle query parameter breaking changes in Whoz API

Error Handling the FP way; using Kotlin