1. Overview

In this tutorial, we’re going to learn how to connect to a NoSQL database using Sprint Boot. For the focus of our article, we’ll be using DataStax Astra DB, a DBaaS powered by Apache Cassandra that allows us to develop and deploy data-driven applications using a cloud-native service.

First, we’ll start by looking at how to get our application set up and configured with Astra DB. Then we’ll learn how to build a simple application using Spring Boot.

2. Dependencies

Let’s start by adding the dependencies to our pom.xml. Of course, we’re going to need the spring-boot-starter-data-cassandra dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-cassandra</artifactId>
    <version>2.6.3</version>
</dependency>

Next, we’ll add the spring-boot-starter-web dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
     <version>2.6.3</version>
</dependency>

Finally, we’ll be using the Datastax astra-spring-boot-starter:

<dependency>
    <groupId>com.datastax.astra</groupId>
    <artifactId>astra-spring-boot-starter</artifactId>
    <version>0.3.0</version>
</dependency>

Now that we have all the necessary dependencies configured, we can start writing our Spring Boot application.

3. Database Setup

Before we start defining our application, it’s important to quickly reiterate that DataStax Astra is a cloud-based database offering that is powered by Apache Cassandra. This gives us a fully hosted, fully managed Cassandra database that we can use to store our data. However, as we’re going to see, the way we set up and connect to our database has some specificities.

In order for us to interact with our database, we need to setup our Astra Database on the host platform. Then, we need to download our Secure Connect Bundle, which contains details of SSL certificates and connection details for this exact database, allowing us to connect securely.

For the purposes of this tutorial, we’ve assumed we’ve done both of these tasks.

4. Application Configuration

Next, we’ll configure a simple main class for our application:

@SpringBootApplication
public class AstraDbSpringApplication {

    public static void main(String[] args) {
        SpringApplication.run(AstraDbSpringApplication.class, args);
    }
}

As we can see, this is a run-of-the-mill Spring Boot application. Now let’s begin to populate our application.properties file:

astra.api.application-token=<token>
astra.api.database-id=<your_db_id>
astra.api.database-region=europe-west1

These are our Cassandra credentials and can be taken directly from the Astra dashboard.

In order to use CQL via a standard CqlSession, we’ll add another couple of properties, including the location of our downloaded secure connect bundle:

astra.cql.enabled=true
astra.cql.downloadScb.path=~/.astra/secure-connect-shopping-list.zip

Finally, we’ll add a couple of standard Spring Data properties for working with Cassandra:

spring.data.cassandra.keyspace=shopping_list
spring.data.cassandra.schema-action=CREATE_IF_NOT_EXISTS

Here, we’re specifying our database keyspace and telling Spring Data to create our tables if they do not exist.

5. Testing Our Connection

Now we have all the parts in place to test our database connection. So let’s go ahead and define a simple REST Controller:

@RestController
public class AstraDbApiController {

    @Autowired
    private AstraClient astraClient;

    @GetMapping("/ping")
    public String ping() {
        return astraClient.apiDevopsOrganizations()
          .organizationId();
    }

}

As we can see, we’ve created a simple ping endpoint using the AstraClient class that will return the organization id of our database. This is a wrapper class that comes as part of the Astra SDK that we can use to interact with various Astra APIs.

Above all, this is just a simple test to make sure we can establish connectivity. So let’s go ahead and run our application using Maven:

mvn clean install spring-boot:run

We should see on our console the connection established with our Astra database:

...
13:08:00.656 [main] INFO  c.d.stargate.sdk.StargateClient - + CqlSession   :[ENABLED]
13:08:00.656 [main] INFO  c.d.stargate.sdk.StargateClient - + API Cql      :[ENABLED]
13:08:00.657 [main] INFO  c.d.stargate.sdk.rest.ApiDataClient - + API Data     :[ENABLED]
13:08:00.657 [main] INFO  c.d.s.sdk.doc.ApiDocumentClient - + API Document :[ENABLED]
13:08:00.658 [main] INFO  c.d.s.sdk.gql.ApiGraphQLClient - + API GraphQL  :[ENABLED]
13:08:00.658 [main] INFO  com.datastax.astra.sdk.AstraClient
  - [AstraClient] has been initialized.
13:08:01.515 [main] INFO  o.b.s.a.AstraDbSpringApplication
  - Started AstraDbSpringApplication in 7.653 seconds (JVM running for 8.097)

Likewise, if we go to our endpoint in a browser or hit it using curl, we should get a valid response:

$ curl http://localhost:8080/ping; echo
d23bf54d-1bc2-4ab7-9bd9-2c628aa54e85

Great! Now that we have our database connection established and a simple application using Spring Boot implemented, let’s see how we can store and retrieve data.

6. Working with Spring Data

We have several flavors to choose from as a basis for our Cassandra database access. In this tutorial, we’ve opted to use Spring Data which has support for Cassandra.

The main goal of Spring Data’s repository abstraction is to significantly reduce the amount of boilerplate code required to implement our data access layer, which will help to keep our example really simple.

For our data model, we’ll define one entity representing a simple shopping list:

@Table
public class ShoppingList {

    @PrimaryKey
    @CassandraType(type = Name.UUID)
    private UUID uid = UUID.randomUUID();

    private String title;
    private boolean completed = false;

    @Column
    private List<String> items = new ArrayList<>();

    // Standard Getters and Setters
}

In this example, we’re using a couple of standard annotations in our bean to map our entity to a Cassandra data table and define a primary key column named uid.

Let’s now create the ShoppingListRepository to be used in our application:

@Repository
public interface ShoppingListRepository extends CassandraRepository<ShoppingList, String> {

    ShoppingList findByTitleAllIgnoreCase(String title);

}

This follows the standard Spring Data repository abstraction. Apart from the inherited methods contained in the CassandraRepository interface, such as findAll, we’ve added an additional method findByTitleAllIgnoreCase that we can use to find a shopping list using the title.

Indeed one of the real benefits of using the Astra Spring Boot Starter is that it creates the CqlSession bean for us using the properties previously defined.

7. Putting It All Together

Now we have our data access repository in place, let’s define a simple service and controller:

@Service
public class ShoppingListService {

    @Autowired
    private ShoppingListRepository shoppingListRepository;

    public List<ShoppingList> findAll() {
        return shoppingListRepository.findAll(CassandraPageRequest.first(10)).toList();
    }

    public ShoppingList findByTitle(String title) {
        return shoppingListRepository.findByTitleAllIgnoreCase(title);
    }
    
    @PostConstruct
    public void insert() {
        ShoppingList groceries = new ShoppingList("Groceries");
        groceries.setItems(Arrays.asList("Bread", "Milk, Apples"));

        ShoppingList pharmacy = new ShoppingList("Pharmacy");
        pharmacy.setCompleted(true);
        pharmacy.setItems(Arrays.asList("Nappies", "Suncream, Aspirin"));

        shoppingListRepository.save(groceries);
        shoppingListRepository.save(pharmacy);
    }
    
}

For the purposes of our test application, we’ve added a @PostContruct annotation to insert some test data into our database.

For the final part of the puzzle, we’ll add a simple controller with one endpoint to retrieve our shopping lists:

@RestController
@RequestMapping(value = "/shopping")
public class ShoppingListController {

    @Autowired
    private ShoppingListService shoppingListService;

    @GetMapping("/list")
    public List<ShoppingList> findAll() {
        return shoppingListService.findAll();
    }
}

Now when we run our application and access http://localhost:8080/shopping/list – we’ll see a JSON response containing our different shopping list objects:

[
  {
    "uid": "363dba2e-17f3-4d01-a44f-a805f74fc43d",
    "title": "Groceries",
    "completed": false,
    "items": [
      "Bread",
      "Milk, Apples"
    ]
  },
  {
    "uid": "9c0f407e-5fc1-41ad-8e46-b3c115de9474",
    "title": "Pharmacy",
    "completed": true,
    "items": [
      "Nappies",
      "Suncream, Aspirin"
    ]
  }
]

This confirms that our application is working properly. Awesome!

8. Working With Cassandra Template

On the other hand, it’s also possible to work directly with the Cassandra Template, the classic Spring CQL approach, and probably still the most popular.

Simply put, we can extend our AstraDbApiController quite easily to retrieve our data center:

@Autowired
private CassandraTemplate cassandraTemplate;

@GetMapping("/datacenter")
public String datacenter() {
    return cassandraTemplate
        .getCqlOperations()
        .queryForObject("SELECT data_center FROM system.local", String.class);
}

This will still leverage all the configuration properties we have defined. So as we can see, switching between the two access methods is entirely transparent.

9. Conclusion

In this article, we learned how to set up and connect to a hosted Cassandra Astra Database. Next, we built a simple shopping list application to store and retrieve data using Spring Data. Finally, we also discussed how we could work with the lower level access method Cassandra Template.

As always, the full source code of the article is available over on GitHub.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.