Using user-defined types (UDT) with Apache Cassandra in Spring Boot 3.x

example with code snippets

ยท

4 min read

Apache Cassandra is a highly scalable and distributed NoSQL database that is well-suited for handling large amounts of data. One of the powerful features of Cassandra is the ability to use user-defined types (UDT) to model complex data structures. In this blog post, we will learn how to use UDTs with Spring Boot 3.x to interact with a Cassandra database.

Creating the data model in cassandra

First, let's create a simple UDT in Cassandra. You can do this by running the following command in the Cassandra shell (imagining test being the cassandra keyspace):

CREATE TYPE test.address (
    street text,
    city text,
    zip text
);

This creates a UDT called "address" with three fields: street, city, and zip.

Next, we will create a table that uses this UDT. For example, we can create a table called "users" with the following command:

CREATE TABLE test.users (
    id int PRIMARY KEY,
    name text,
    address frozen<address>
);

This table has three columns: id, name, and address. The "address" column is the UDT type here. When defining a user-defined type column, it is best practice to utilize the frozen keyword. This ensures that the user-defined type value cannot be partially updated, but must be completely overwritten. By using the 'frozen' keyword, Cassandra treats the value of the user-defined type as a single, immutable block of data, similar to a binary large object (BLOB).

Setup spring boot 3.x application

Now that we have our UDT and table set up, we can start using them with Spring Boot. Head to start.spring.io to create a spring boot 3.x project with cassandra or add the following dependency to the existing spring boot projects.

Gradle:

  implementation("org.springframework.boot:spring-boot-starter-data-cassandra")

Maven:

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

We also need to configure the Cassandra connection in our application.yml file:

spring:
  data:
    cassandra:
      contact-points: localhost
      port: 9042
      keyspace-name: test

Setup data model in Spring boot application

Once we have the dependency and configuration in place, We also need to create a POJO class that represents our "users" table:

import org.springframework.data.cassandra.core.mapping.PrimaryKey
import org.springframework.data.cassandra.core.mapping.Table

@Table
data class User (
    @PrimaryKey
    val id: Int,
    val name: String,
    /**
     * @CassandraType annotation is not required by default and is redundant. but can be added if requried.
     * @CassandraType(type = CassandraType.Name.UDT, userTypeName = "address")
     */
    val address: Address
)

And a POJO class that represents our address UDT:

import org.springframework.data.cassandra.core.mapping.UserDefinedType

@UserDefinedType("address")
data class Address (
    val street: String,
    val city: String,
    val zip: String
)

we also need to create a Cassandra repository to interact with our users table. For example:

import org.springframework.data.cassandra.repository.CassandraRepository

interface UserRepository : CassandraRepository<User, Int>

Use the repository to call Cassandra

With these classes in place, we can now use the UserRepository to perform CRUD operations on the users table. For example, we can save a new user with:

@Configuration
class App {
    @Bean
    fun runner(userRepository: UserRepository) = ApplicationRunner{
        val user = User(1, "John Doe", Address("Street name", "Berlin", "12345"))
        userRepository.save(user)
    }
}

Conclusion

We hope this tutorial gave you a good idea on how to use user-defined types (UDT) with Apache Cassandra in Spring Boot 3.x. We've shown you how to create a UDT and table in Cassandra, and how to use them with Spring Boot to perform CRUD operations on the table.

Using UDTs can be a powerful way to model complex data structures in Cassandra, and Spring Boot makes it easy to work with these structures in a Java application. With the right configuration and setup, you can easily start using UDTs in your projects to improve the scalability and performance of your data storage.

Be sure to follow best practices when working with UDTs, such as using frozen types and avoiding unnecessary nesting to minimize the complexity. Remember also to keep in mind that UDTs are not supported in some version of Cassandra and not all the drivers support it.

As always, feel free to leave a comment or reach out if you have any questions or need further help. Happy coding!

Did you find this article valuable?

Support Driptaroop Das by becoming a sponsor. Any amount is appreciated!

ย