Using DynamoDBMapper within an AWS Lambda (i.e. not Android) written in Kotlin, I can save a record using a data class. However when I attempt to load a record to a data class, I receive a "DynamoDBMappingException: could not instantiate class" exception.
#DynamoDBTable(tableName = "Test")
data class TestItem(
#DynamoDBHashKey(attributeName="someKey")
#DynamoDBAttribute(attributeName = "someKey")
var someKey: String?,
#DynamoDBAttribute(attributeName = "someValue")
var someValue: String?
}
val ddbMapper = DynamoDBMapper(AmazonDynamoDBClientBuilder.defaultClient())
ddbMapper.load(TestItem::class.java, "xyz")
Results in the following exception:
com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException:
could not instantiate class
com.intuit.connect_to_pro.lambda_common_core.aws_service.TestItem
With the root exception being:
java.lang.NoSuchMethodException:
com.intuit.connect_to_pro.lambda_common_core.aws_service.TestItem.()
AWS has an example for Android that uses com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBMapper instead of com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper. I tried the Android version, but the result was the same.
https://docs.aws.amazon.com/aws-mobile/latest/developerguide/add-aws-mobile-nosql-database.html
Any help would be appreciated.
The DynamoDBMapper expects a class with an empty constructor. Using a Kotlin data class, you can specify default values for all parameters and use #JvmOverload, which will generate the empty constructor for JVM (Java). Also all parameters need to be mutable, so you need to use "var" instead of "val".
#DynamoDBTable(tableName = "Test")
data class TestItem #JvmOverloads constructor(
#DynamoDBHashKey(attributeName="someKey")
var someKey: String = "",
var someValue: String = ""
)
Make sure that all your classes have an empty constructor. In my case I had nested documents. Those had to have empty constructors too.
In Kotlin, an empty (parameterless) constructor will be created if you specify default values for all the attributes.
Also, make sure that the data from the db can be converted to the data in your classes.
For example, mine failed because I had an Integer property in my class while in the db I had a String. i.e. I had the String value "30" in the db, instead of the Integer value 30.
Related
I know how to use the apply function on a normal Kotlin class but have not been able to use it with a data class:
data class Person(name: String)
val person = Person().apply {
name = "Tony Stark"
}
I get a compile message of:
No value passed for parameter 'name'
The issue is that name is a constructor parameter only and not made a property, which is invalid for the data class concept anyway. Fix like this:
data class Person(val name: String)
The apply function works similar with any class. But there are some errors in your code snippet:
Parameter in Person constructor didn't mentioned as var or val, so there is no fields name in that class. It would be better to make it var to be able to change value.
You made class's constructor with 1 parameter, but trying to use empty constructor - it is error.
Sorry for asking a very newbie Kotlin question, but I'm struggling to understand some of the things related to constructors and intitializing.
I have this class and constructor:
class TestCaseBuilder constructor(
caseTag: String = "Case",
applType: Buy.ApplFor = Buy.ApplFor.PROOFFINANCE,
komnr: String = "5035") {
var caseTag: String = caseTag
var applType: Buy.ApplFor = applType
var komnr: String = komnr
What I'm trying to do here is to have three optional parameters in the constructors, using default values for them. The reason I'm declaring them in the class body is because I need to have access to them from the main class.
Now, this code works. No errors when I run it. But IntelliJ gives the following comment for the variables (ex.: caseTag):
Property is explicitly assigned to parameter caseTag, can be declared
directly in constructor.
What I've found when searching this is examples using an init {}, but the result I've gotten to includes initializing the variables twice, once in the constructor and then in the init {}. Which clearly isn't correct, I'd say?
What's a better what to have (or than having) optional parameters in the constructor, and then creating class variables from them?
You can declare properties directly in primary constructor. That means you can drop explicit declarations in class body:
class TestCaseBuilder constructor(
var caseTag: String = "Case",
var applType: Buy.ApplFor = Buy.ApplFor.PROOFFINANCE,
var komnr: String = "5035")
You can also drop the constructor keyword if your primary constructor does not have any annotations or visibility modifiers (defaults to public).
#JvmOverloads annotation can over load the constructor with different param size
class TestCaseBuilder #JvmOverloads constructor(
var caseTag: String = "Case",
var applType: Buy.ApplFor = Buy.ApplFor.PROOFFINANCE,
var komnr: String = "5035"
)
Then the class got three constructor with optional param
val a = TestCaseBuilder("CaseA")
val b = TestCaseBuilder("CaseB", Buy.ApplFor.SomethingElse)
val c = TestCaseBuilder("CaseB", Buy.ApplFor.SomethingElse, "1111")
I followed the tutorial http://www.baeldung.com/spring-data-rest-relationships.
I also observed that I can create the association directly by providing the link to the relationship.
curl -i -X POST -H "Content-Type:application/json" -d '{"name":"My Library"}' http://localhost:8080/libraries
curl -i -X POST -d '{"title":"Books", "library":"http://localhost:8080/libraries/1"}' -H "Content-Type:application/json" http://localhost:8080/books
This works fine in Java and also in Kotlin when using a regular class.
However, if I use a data class in Kotlin, I get the following error
2018-04-26 14:13:43.730 ERROR 79256 --- [nio-8080-exec-2] b.e.h.RestResponseEntityExceptionHandler : org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of com.baeldung.models.Library (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/libraries/1'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.baeldung.models.Library (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/libraries/1') at [Source: (org.apache.catalina.connector.CoyoteInputStream); line: 1, column: 29] (through reference chain: com.baeldung.models.Book["library"])
I do have the relevant kotlin-spring, kotlin-jpa and kotlin-noarg plugins in my project.
Code is here https://github.com/vijaysl/spring-data-rest
Try adding #JsonCreator(mode = JsonCreator.Mode.DISABLED) annotation on primary constructor. No need to disable the com.fasterxml.jackson.module:jackson-module-kotlin.
Explanation:
Kotlin Jackson module implies your default constructor is the JSON creator (see KotlinValueInstantiator class).
Therefore, Spring Data REST does not apply its bean deserializer modifier (that is supposed to load a bean by URI) because bean properties mappings are not used for creator properties (constructor params).
KotlinValueInstantiator tries to deserialize constructor params using standard deserializers and instantiators and this leads to the error you mentioned.
Possible solution:
Since koltin-jpa module adds a default empty constructor for JPA, you can instruct Jackson not to use the JSON creator but the default empty constructor by explicitly disabling it.
Example:
#Entity
class Book #JsonCreator(mode = JsonCreator.Mode.DISABLED) constructor(
#ManyToMany
val libraries: ModifiableList<Library> = ArrayList(),
): AbstractPersistable<Long>(), Identifiable<Long>
Kotlin data classes are pretty strict. It's telling you, basically, it can't construct your POKO and it's listing some of the ways it tries. One of them is with a String constructor. Others are through private field manipulation (which is the way it's been done normally).
Data classes in kotlin, if they have fields declared as private val name:String translate to (in java) private final String name; It can't assign to a final field (which is dirty to try to assign to a private field, but impossible when it's final; the JVM won't allow it) and there are no getName() or setName() functions which can be used as another method of hydration.
Some options:
Declare your variables are var instead of val. private var name:String is java equiavalent to private String name which will use field based (dirty) hydration.
include a specific kotlin dependency for kotlin that fixes this issue: compile("com.fasterxml.jackson.module:jackson-module-kotlin") have a look at this project
example kotlin class that should work for you:
import org.springframework.hateoas.Identifiable
import java.time.LocalDate
import javax.persistence.*
import javax.validation.constraints.*
#Entity
data class Employee(#Pattern(regexp = "[A-Za-z0-9]+")
#Size(min = 6, max = 32)
val name: String,
#Email
#NotNull
val email: String?,
#PastOrPresent
val hireDate: LocalDate = LocalDate.now(),
#OneToMany(mappedBy = "employee", cascade = [CascadeType.ALL])
val forms:List<Form> = listOf(),
#OneToMany(mappedBy = "employee", cascade = [CascadeType.ALL])
val reports:List<Report> = listOf(),
#Id #GeneratedValue( strategy = GenerationType.IDENTITY) private val id: Long? = null): Identifiable<Long> {
override fun getId() = id
constructor(name:String): this(name,"$name#foo.com")
}
With kotlin all Ok.
Just replace "data class" to "class".
Jackson don't find empty constructor in "data class". And use other deserializator... not Uri....
I have a Kotlin data class that I am constructing with many immutable properties, which are being fetched from separate SQL queries. If I want to construct the data class using the builder pattern, how do I do this without making those properties mutable?
For example, instead of constructing via
var data = MyData(val1, val2, val3)
I want to use
builder.someVal(val1)
// compute val2
builder.someOtherVal(val2)
// ...
var data = builder.build()
while still using Kotlin's data class feature and immutable properties.
I agree with the data copy block in Grzegorz answer, but it's essentially the same syntax as creating data classes with constructors. If you want to use that method and keep everything legible, you'll likely be computing everything beforehand and passing the values all together in the end.
To have something more like a builder, you may consider the following:
Let's say your data class is
data class Data(val text: String, val number: Int, val time: Long)
You can create a mutable builder version like so, with a build method to create the data class:
class Builder {
var text = "hello"
var number = 2
var time = System.currentTimeMillis()
internal fun build()
= Data(text, number, time)
}
Along with a builder method like so:
fun createData(action: Builder.() -> Unit): Data {
val builder = Builder()
builder.action()
return builder.build()
}
Action is a function from which you can modify the values directly, and createData will build it into a data class for you directly afterwards.
This way, you can create a data class with:
val data: Data = createData {
//execute stuff here
text = "new text"
//calculate number
number = -1
//calculate time
time = 222L
}
There are no setter methods per say, but you can directly assign the mutable variables with your new values and call other methods within the builder.
You can also make use of kotlin's get and set by specifying your own functions for each variable so it can do more than set the field.
There's also no need for returning the current builder class, as you always have access to its variables.
Addition note: If you care, createData can be shortened to this:
fun createData(action: Builder.() -> Unit): Data = with(Builder()) { action(); build() }.
"With a new builder, apply our action and build"
I don't think Kotlin has native builders. You can always compute all values and create the object at the end.
If you still want to use a builder you will have to implement it by yourself. Check this question
There is no need for creating custom builders in Kotlin - in order to achieve builder-like semantics, you can leverage copy method - it's perfect for situations where you want to get object's copy with a small alteration.
data class MyData(val val1: String? = null, val val2: String? = null, val val3: String? = null)
val temp = MyData()
.copy(val1 = "1")
.copy(val2 = "2")
.copy(val3 = "3")
Or:
val empty = MyData()
val with1 = empty.copy(val1 = "1")
val with2 = with1.copy(val2 = "2")
val with3 = with2.copy(val3 = "3")
Since you want everything to be immutable, copying must happen at every stage.
Also, it's fine to have mutable properties in the builder as long as the result produced by it is immutable.
It's possible to mechanize the creation of the builder classes with annotation processors.
I just created ephemient/builder-generator to demonstrate this.
Note that currently, kapt works fine for generated Java code, but there are some issues with generated Kotlin code (see KT-14070). For these purposes this isn't an issue, as long as the nullability annotations are copied through from the original Kotlin classes to the generated Java builders (so that Kotlin code using the generated Java code sees nullable/non-nullable types instead of just platform types).
I was creating a sample Android project using Kotlin programming language using this blog. I'm new to Kotlin programming. I came across this line,
data class Cats(var data: Data? = null)
which I believe that it is, creating a class named Cats that has a variable named data. What does this data: Data? = null means? My whole class model is:
data class Cats(var data: Data? = null)
data class Data(var images: ArrayList<Image>? = null)
data class Image(var url: String? = "", var id: String? = "", var source_url: String? = "")
You are right that Cats class will have a mutable property named data, since it is declared inside a primary constructor of a class with var keyword (see the docs pages about classes and properties).
Next, question mark at type name in Kotlin means that a variable has nullable type, that is, it can store null value. Variables with not-null types, on the other hand, cannot hold nulls in Kotlin.
data: Data? = null is syntax for default parameter value. It allows not to pass data parameter to the constructor invocation, and in this case default value null will be used. This also works for functions.
And finally, data modifier at class declaration means that equals, hashCode, toString, copy and destructuring will be generated for the class, based on the properties declared in primary constructor.
=> Classes in Kotlin are declared using the keyword class:
class Invoice { }
=> Both the header and the body are optional; if the class has no body, curly braces can be omitted.
class Invoice