Hi Channel! I don’t know if this is the right pla...
# spring
a
Hi Channel! I don’t know if this is the right place as my question is related to AWS dynamodb with the V2 enhanced client (of course in Spring Boot + Kotlin). I’m following this blog post. I keep getting this exception from the dynamodb SDK when I try to write to dynamo -
java.lang.IllegalArgumentException: Attempt to execute an operation that requires a primary index without defining any primary key attributes in the table metadata.
. I define partition key in the data class. But it keeps complaining that I don’t have it!. Here’s the entire code:
Copy code
package com.redacted.backend.monolith.app.controllers

import com.redacted.backend.monolith.config.dynamodb.createDynamoDbClient
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.graphql.data.method.annotation.Argument
import org.springframework.graphql.data.method.annotation.MutationMapping
import org.springframework.stereotype.Controller
import org.springframework.stereotype.Repository
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient
import software.amazon.awssdk.enhanced.dynamodb.TableSchema
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey

@Controller
class DogController(private val repo: DogRepository) {

    @MutationMapping
    fun createDog(@Argument dog: Dog): String {
        return repo.create(dog)
    }

}

@DynamoDbBean
data class Dog(
    @get:DynamoDbPartitionKey
    val dogId: String? = null,
    @get:DynamoDbSortKey
    val rating: Int = 0,
    val name: String? = null
)

@Configuration
class DynamoClient(
    @Value("aws.dynamodb.access-key") private val dynamoDbAccessKey: String,
    @Value("aws.dynamodb.secret-key") private val dynamoDbSecretKey: String,
    @Value("aws.dynamodb.region") private val dynamoDbRegion: String
) {

    @Bean
    fun dynamoDbClient(): DynamoDbEnhancedClient = createDynamoDbClient(
        accessKey = dynamoDbAccessKey,
        secretKey = dynamoDbSecretKey,
        region = dynamoDbRegion
    )

}

@Repository
class DogRepository(private var client: DynamoDbEnhancedClient) {

    private val table = client.table("dog_table", TableSchema.fromBean(Dog::class.java))
    fun create(dog: Dog): String {
        table.putItem(dog)
        println("Written the dog to db successfully!")
        return "Written"
    }
}
Here’s the whole error: Any help would be very much appreciated! Error message is in the 🧵
Here’s the whole error:
Copy code
java.lang.IllegalArgumentException: Attempt to execute an operation that requires a primary index without defining any primary key attributes in the table metadata.
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableMetadata.getIndex(StaticTableMetadata.java:146) ~[dynamodb-enhanced-2.20.21.jar:na]
	at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableMetadata.indexPartitionKey(StaticTableMetadata.java:83) ~[dynamodb-enhanced-2.20.21.jar:na]
	at software.amazon.awssdk.enhanced.dynamodb.TableMetadata.primaryPartitionKey(TableMetadata.java:123) ~[dynamodb-enhanced-2.20.21.jar:na]
	at software.amazon.awssdk.enhanced.dynamodb.internal.operations.PutItemOperation.generateRequest(PutItemOperation.java:86) ~[dynamodb-enhanced-2.20.21.jar:na]
	at software.amazon.awssdk.enhanced.dynamodb.internal.operations.PutItemOperation.generateRequest(PutItemOperation.java:45) ~[dynamodb-enhanced-2.20.21.jar:na]
	at software.amazon.awssdk.enhanced.dynamodb.internal.operations.CommonOperation.execute(CommonOperation.java:113) ~[dynamodb-enhanced-2.20.21.jar:na]
	at software.amazon.awssdk.enhanced.dynamodb.internal.operations.TableOperation.executeOnPrimaryIndex(TableOperation.java:59) ~[dynamodb-enhanced-2.20.21.jar:na]
	at software.amazon.awssdk.enhanced.dynamodb.internal.client.DefaultDynamoDbTable.putItem(DefaultDynamoDbTable.java:201) ~[dynamodb-enhanced-2.20.21.jar:na]
	at software.amazon.awssdk.enhanced.dynamodb.internal.client.DefaultDynamoDbTable.putItem(DefaultDynamoDbTable.java:209) ~[dynamodb-enhanced-2.20.21.jar:na]
	at software.amazon.awssdk.enhanced.dynamodb.internal.client.DefaultDynamoDbTable.putItem(DefaultDynamoDbTable.java:214) ~[dynamodb-enhanced-2.20.21.jar:na]
	at com.redacted.backend.monolith.app.controllers.DogRepository.create(DogController.kt:57) ~[main/:na]
	at com.redacted.backend.monolith.app.controllers.DogController.createDog(DogController.kt:22) ~[main/:na]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:578) ~[na:na]
	at org.springframework.graphql.data.method.InvocableHandlerMethodSupport.doInvoke(InvocableHandlerMethodSupport.java:87) ~[spring-graphql-1.1.2.jar:1.1.2]
	at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.validateAndInvoke(DataFetcherHandlerMethod.java:183) ~[spring-graphql-1.1.2.jar:1.1.2]
	at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.invoke(DataFetcherHandlerMethod.java:116) ~[spring-graphql-1.1.2.jar:1.1.2]
	at org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer$SchemaMappingDataFetcher.get(AnnotatedControllerConfigurer.java:530) ~[spring-graphql-1.1.2.jar:1.1.2]
	at org.springframework.graphql.execution.ContextDataFetcherDecorator.lambda$get$0(ContextDataFetcherDecorator.java:76) ~[spring-graphql-1.1.2.jar:1.1.2]
	at io.micrometer.context.ContextSnapshot.lambda$wrap$1(ContextSnapshot.java:92) ~[context-propagation-1.0.2.jar:1.0.2]
	at org.springframework.graphql.execution.ContextDataFetcherDecorator.get(ContextDataFetcherDecorator.java:76) ~[spring-graphql-1.1.2.jar:1.1.2]
	at graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation.lambda$instrumentDataFetcher$0(DataLoaderDispatcherInstrumentation.java:86) ~[graphql-java-19.2.jar:na]
	at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:282) ~[graphql-java-19.2.jar:na]
	at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:211) ~[graphql-java-19.2.jar:na]
	at graphql.execution.ExecutionStrategy.resolveField(ExecutionStrategy.java:183) ~[graphql-java-19.2.jar:na]
	at graphql.execution.AsyncSerialExecutionStrategy.lambda$execute$1(AsyncSerialExecutionStrategy.java:47) ~[graphql-java-19.2.jar:na]
	at graphql.execution.Async.eachSequentiallyImpl(Async.java:191) ~[graphql-java-19.2.jar:na]
	at graphql.execution.Async.eachSequentially(Async.java:180) ~[graphql-java-19.2.jar:na]
	at graphql.execution.AsyncSerialExecutionStrategy.execute(AsyncSerialExecutionStrategy.java:42) ~[graphql-java-19.2.jar:na]
	at graphql.execution.Execution.executeOperation(Execution.java:159) ~[graphql-java-19.2.jar:na]
	at graphql.execution.Execution.execute(Execution.java:105) ~[graphql-java-19.2.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:645) ~[graphql-java-19.2.jar:na]
	at graphql.GraphQL.lambda$parseValidateAndExecute$11(GraphQL.java:564) ~[graphql-java-19.2.jar:na]
	at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2341) ~[na:na]
	at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:559) ~[graphql-java-19.2.jar:na]
	at graphql.GraphQL.executeAsync(GraphQL.java:527) ~[graphql-java-19.2.jar:na]
	at org.springframework.graphql.execution.DefaultExecutionGraphQlService.lambda$execute$2(DefaultExecutionGraphQlService.java:82) ~[spring-graphql-1.1.2.jar:1.1.2]
	at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:47) ~[reactor-core-3.5.3.jar:3.5.3]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4485) ~[reactor-core-3.5.3.jar:3.5.3]
	at reactor.core.publisher.Mono.subscribeWith(Mono.java:4551) ~[reactor-core-3.5.3.jar:3.5.3]
	at reactor.core.publisher.Mono.toFuture(Mono.java:5060) ~[reactor-core-3.5.3.jar:3.5.3]
	at org.springframework.core.ReactiveAdapterRegistry$ReactorRegistrar.lambda$registerAdapters$5(ReactiveAdapterRegistry.java:244) ~[spring-core-6.0.6.jar:6.0.6]
	at org.springframework.core.ReactiveAdapter.fromPublisher(ReactiveAdapter.java:121) ~[spring-core-6.0.6.jar:6.0.6]
	at org.springframework.web.servlet.function.DefaultAsyncServerResponse.create(DefaultAsyncServerResponse.java:187) ~[spring-webmvc-6.0.6.jar:6.0.6]
	at org.springframework.web.servlet.function.ServerResponse.async(ServerResponse.java:250) ~[spring-webmvc-6.0.6.jar:6.0.6]
	at org.springframework.graphql.server.webmvc.GraphQlHttpHandler.handleRequest(GraphQlHttpHandler.java:107) ~[spring-graphql-1.1.2.jar:1.1.2]
	at org.springframework.web.servlet.function.support.HandlerFunctionAdapter.handle(HandlerFunctionAdapter.java:107) ~[spring-webmvc-6.0.6.jar:6.0.6]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081) ~[spring-webmvc-6.0.6.jar:6.0.6]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974) ~[spring-webmvc-6.0.6.jar:6.0.6]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011) ~[spring-webmvc-6.0.6.jar:6.0.6]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.0.6.jar:6.0.6]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:731) ~[tomcat-embed-core-10.1.5.jar:6.0]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.0.6.jar:6.0.6]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814) ~[tomcat-embed-core-10.1.5.jar:6.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-10.1.5.jar:10.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.0.6.jar:6.0.6]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.6.jar:6.0.6]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.0.6.jar:6.0.6]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.6.jar:6.0.6]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.0.6.jar:6.0.6]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.6.jar:6.0.6]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:859) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1734) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.5.jar:10.1.5]
	at java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314) ~[na:na]
	at java.base/java.lang.VirtualThread.run(VirtualThread.java:287) ~[na:na]
	at java.base/java.lang.VirtualThread$VThreadContinuation.lambda$new$0(VirtualThread.java:174) ~[na:na]
	at java.base/jdk.internal.vm.Continuation.enter0(Continuation.java:327) ~[na:na]
	at java.base/jdk.internal.vm.Continuation.enter(Continuation.java:320) ~[na:na]
v
I'm not very familiar with DynamoDB but it might have something to do with the use-site target annotation? You use
@get:DynamoDbPartitionKey
on the property, and the
@get:
will apply the annotation only to the underlying getter method which may not be where the spring/dynamodb library expects it to be.
a
The annotation is applied to the getter functions in Java POJOs. That would make the above one correct..
532 Views