1

Set up

I am working on a Spring Boot project where we would like to make use of DynamoDB.
Instead of using the AWS library directly, we would like to use JNOSQL; this API allows to easily switch databases by selecting a different driver, which we may want to do in the future.

In our project, I created a small standalone configuration class to test the library by following the instructions in this tutorial and following the official docs for a Key-Value DB (here).

The docs show that, once configured the database, I should be able to @Produces a BucketManager, this would allow me to then @Inject a KeyValueTemplate in my database service to write to the DB.

These are CDI annotations, with which I am not too familiar; this said, I am aware that @Inject is something that Spring understands, while this may not be the case for @Produces, which could be replaced with @Bean

The issue

By simply following the docs, whenever I try to run the application, Spring complaints that no KeyValueTemplate bean can be found:

Field template in com.max.backend.service.TestEntryService required a bean of type 'jakarta.nosql.mapping.keyvalue.KeyValueTemplate' that could not be found.

Rightfully so: I am declaring a bean for BucketManager, not for KeyValueTemplate.
Notice that the docs show a sample app in which an SeContainer is created, something I did not think I would need given that I am using Spring.

My hunch

I think this may have something to do with CDI vs Spring, where the library is somehow creating a bean for KeyValueTemplate when I @Produces the BucketManager, but Spring does not see this bean.

Ideally I would like to either make this work fully with Spring, avoiding CDI altogether, or find a comprehensive and minimal solution to leverage the KeyValueTemplate

I'm surprised I did not find much about this online, this is a pretty established and useful library, I find peculiar that there are not many usages in Spring.
If you know a similar lib for Spring, please let me know!!

My code

Note: a full example of a JNOSQL demo for a KeyValue db (not for Spring), can be seen here.

Relevant Dependencies in build.gradle

 // Spring
 implementation 'org.springframework.boot:spring-boot-starter-web'
 compile group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: '2.3.5.RELEASE'

 // JNOSQL
 implementation group: 'org.eclipse.jnosql.artemis', name: 'artemis-key-value', version: '1.0.0-b1'
 implementation group: 'org.eclipse.jnosql.diana', name: 'dynamodb-driver', version: '1.0.0-b1'
 
 // JSON parsing
 implementation group: 'javax.json.bind', name: 'javax.json.bind-api', version: '1.0'
 implementation group: 'org.eclipse', name: 'yasson', version: '1.0.4'
 implementation group: 'org.glassfish', name: 'javax.json', version: '1.1'

 // For CDI (needed primarily for annotations)
 implementation group: 'javax', name: 'javaee-web-api', version: '8.0'

Configuration MaxDatabase.java

package com.max.backend.service.jnosql;


import jakarta.nosql.Settings;
import jakarta.nosql.keyvalue.BucketManager;
import org.eclipse.jnosql.diana.dynamodb.keyvalue.DynamoDBBucketManagerFactory;
import org.eclipse.jnosql.diana.dynamodb.keyvalue.DynamoDBKeyValueConfiguration;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import java.util.HashMap;
import java.util.Map;

@Configuration
@ApplicationScoped
public class MyDatabase {

    private DynamoDBKeyValueConfiguration configuration;
    private DynamoDBBucketManagerFactory managerFactory;

    @PostConstruct
    public void initialize() {
        // Set up the DB
        configuration = new DynamoDBKeyValueConfiguration();
        Map<String, Object> settings = new HashMap<>();
        settings.put("dynamodb.endpoint", "http://localhost:8073");
        settings.put("dynamodb.region", "eu-west-1");
        settings.put("dynamodb.awsaccesskey", "myLocalKeyId");
        settings.put("dynamodb.secretaccess", "myLocalSecretAccessKey");
        managerFactory = configuration.get(Settings.of(settings));
    }

    @Produces // use @Bean instead?
    public BucketManager getManager() {
        return managerFactory.getBucketManager("initiatives");
    }
}

Service

package com.max.backend.service;

import com.max.backend.model.TestEntry;
import jakarta.nosql.mapping.keyvalue.KeyValueTemplate;
import org.springframework.stereotype.Service;

import javax.inject.Inject;
import java.util.Optional;

@Service
public class TestEntryService {

    @Inject
    private KeyValueTemplate template;

    public void put(TestEntry user) {
        template.put(user);
    }

    public Optional<TestEntry> find(String id) {
        return template.get(id, TestEntry.class);
    }
}

(TestEntry is just a small POJO, irrelevant to the issue I am facing here)

Tom Ryan
  • 397
  • 2
  • 6
  • 26
  • Writing DB configuration in a Java class is a throwback from the old days when Spring was centered around XML bean declarations. Thank god those days are gone! This is still a thing, but it is usually done behind the scenes by the framework. Unless, of course, you enjoy typing. In that case, all of these legacy features are still included with SB. – Nate T Aug 09 '21 at 01:40

2 Answers2

2

Going on the description of events, I have a feeling that you may not realize that you are doing things the hard way.

That is, you do not need to write Java code for your databases. Spring boot offers a feature almost exactly like the one you are trying to implement. Although I have little experience with JNOSQL, I used to build Android applications so I am all too familiar with the gradle syntax.

For that reason, I can confidently say that the solution native to Spring boot is about the same amount of work.

I can tell you that using an application.properties files as seen here will cost you four lines of code per database, so long as you keep the configuration basic. From there, the idea is to add directives as needed, and at your own discretion, as I'm sure it is in JNOSQL as well.

Have a look at the link above. I have a feeling that, once you realize that the feature is available in Spring, you'll decide to go with it.

The first two videos linked here (and creator) are excellent sources on the subject.

The second is actually a 15-video play list, which shows the actual depth of the built-in config system.

And these files do not just configure databases. In fact, one of the reasons that Spring Boot is so popular on the microservices front at the moment is because all of the configurations can be listed within a single config file.

Nate T
  • 727
  • 5
  • 21
1

I believe that the error says it all, you need to create the @Bean KeyValueTemplate. See https://docs.spring.io/spring-data/keyvalue/docs/current/reference/html/#key-value.template-configuration for a quick example.

SeContainer is not needed in Spring as you thought. It is WildFly implementation of CDI. But, not being an expert in WildFly, I believe the example shows how the CDI config is done with KeyValueTemplate keyValueTemplate = container.select(KeyValueTemplate.class).get(); (that would be the same as the @Beans in the example in the URL above)

grekier
  • 2,322
  • 1
  • 16
  • 23