Guide to Cassandra Object Mapper with Spring

Guide to Cassandra Object Mapper with Spring


1. Overview

This article will focus on setting up Spring to store and fetch data from Apache Cassandra to Java Object using Object Mapper.

We'll start with the basics and will go through the configuration of a practical example demonstrating conversion from any Java Object to Apache Cassandra UDT. If this is your first time using Spring with Apache Cassandra, maybe this article will get you up to speed: http://www.baeldung.com/spring-data-cassandratemplate-cqltemplate. And if you need help with Cassandra data modelling: http://www.baeldung.com/cassandra-data-modeling.

2. Maven Dependencies

We'll start by adding maven dependencies in the pom.xml:

<dependency>
    <groupId>com.datastax.cassandra</groupId>
    <artifactId>cassandra-driver-core</artifactId>
    <version>3.2.0</version>
</dependency>

<dependency><groupId>com.datastax.cassandra</groupId><artifactId>cassandra-driver-mapping</artifactId><version>3.2.0</version>
</dependency>

3. Cassandra User-Defined Types(UDT)

We'll use datastax driver that provides a simple object mapper, handling basic CRUD operations in Cassandra UDT and collections. Here we'll see how to store Java Objects and Collections to Cassandra UDT. You can store nested objects using Cassandra User Defined Type(UDT).

3.1 Declaring a Cassandra Type

Let's start with declaring a Cassandra UDT which we'll use in our main Table. UDT can be nested into another one, given former is declared before latter.

CREATE TYPE gym.owner_info(
    name text,
    phone_number text,
    address text,
)

3.2 Declaring Cassandra Table

Next step we'll define the Table gym_by_city in Cassandra containing information about gyms spread out worldwide. In this example, we'll include UDT shown above as well as a collection. The frozen keyword is used

CREATE TABLE gym.gym_by_city(
     country_code text,
     state text,
     city text,
     gym_name text,
     opening_date timestamp,
     owner frozen<owner_info>,
     revenue_by_year map<int, decimal>,
     PRIMARY KEY((country_code, state, city), opening_date, gym_name))
     WITH CLUSTERING ORDER BY (opening_date DESC, gym_name ASC);

4. The Entity

Let's see how to define the POJO class to store and manipulate Cassandra data. Using annotations, Cassandra tables are mapped to this Java Object. First one is the entity class for UDT and the second one is for 'gym_by_city' table.


@UDT(keyspace = "gym", name = "owner_info")
public class OwnerInfo{

   @Field(name = "name")
   private String name;

   @Field(name = "phone_number")
   private String phoneNumber;

   @Field(name = "address")
   private String address;

   //Standard getters and setters
}

NOTE: Timestamp data type is from java.sql.Timestamp

@Table(keyspace = "gym", name="gym_by_city")
public class GymByCity{
    
    @PartitionKey(0)
    @Column(name = "country_code")
    private String countryCode;

    @PartitionKey(1)
    @Column(name = "state")
    private String state;
 
    @PartitionKey(2)
    @Column(name = "column")
    private String city;

    @ClusteringColum(1)
    @Column(name = "gym_name")
    private String gymName;
    
    @ClusteringColumn(0)
    @Column(name = "opening_date")
    private Timestamp openingDate;

    @Frozen@Column(name = "owner")
    private OwnerInfo owner;
    
    @Frozen@Column(name = "revenue_by_year")
    private HashMap<Integer, Decimal> revenueByYear;

    //Standard getters and setters
}

Frozen serializes UDT and Cassandra collections into a single value and doesn't allow updates to individual fields. Cassandra treats the value of frozen type as a blob.

5. Entity Mapper

We'll use Mapping Manager to query data from Cassandra to Java Objects. Mapping Manager is thread-safe and can be safely shared throughout the application. We'd typically create one instance at startup, right after our session. Each Entity Class is managed by a dedicated Mapper Object. We'll see how to perform basic query operations using Mapper.

public void saveGymByCity(GymByCity gymData){
   MappingManager manager = new MappingManager(cassandraTemplate.getSession());
   Mapper<GymByCity> mapper = manager.mapper(GymByCity.class);
   
   mapper.save(gymData);
}

This will save Java object to Cassandra using Object Mapper.

public List<GymByCity> getGymListGivenCountryAndState(String countryCode, String state){
    MappingManager manager = new MappingManager(cassandraTemplate.getSession());
    Mapper<GymByCity> mapper = manager.mapper(GymByCity.class);

    Select select = QueryBuilder.select.all().from("gym_by_city");
    select.where(QueryBuilder.eq("country_code",countryCode))
    .and(QueryBuilder.eq("state",state));
    select.allowFiltering();

    ResultSet resultSet = cassandraTemplate.getSession().execute(select);
    Result<GymByCity> list = mapper.map(resultSet);

    return list.all();
    //return list.one() in case of unique result.
 }

Result<> is iterable type so accessing will exhaust the data from it. Above example will give List of required entries. Mapper Object provides support for Asynchronous equivalents.

6. Conclusion

This tutorial covered basic steps of utilizing User-Defined Types and Collections using Cassandra database with Spring.

Thanks brother for the article, I tried it on my own end i was getting an error. "Unknown type [class com.quickschinfo.models.Test2] for property [test2] in entity [com.quickschinfo.models.Test]; only primitive types and Collections or Maps of primitive types are allowed", This are my sample code bellow. maven dependency: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-cassandra</artifactId> </dependency> <dependency> <groupId>com.datastax.cassandra</groupId> <artifactId>cassandra-driver-mapping</artifactId> <version>3.1.4</version> </dependency> Cassandra repository: public interface DataRepository extends CassandraRepository<Test, String> { } Test model class: @Table(keyspace = "quickschoolinfo", name="test") public class Test { @PartitionKey(0) @Column(name = "id") public int id; @Column(name = "number") public long number; @Column(name = "bool") public boolean bool; @PartitionKey(1) @Column(name = "amount") public double amount; @Column(name = "character") public char character; @Column(name = "name") public String name; @Frozen @Column(name = "test2") public Test2 test2; @Frozen @Column(name = "test2List") public List<Test2> test2List; @Column(name = "timestamp") public Date timestamp; } Test2 model class: @Table(keyspace = "quickschoolinfo", name="test2") public class Test2 { @Field(name = "id") public int id; @Field(name = "number") public long number; @Field(name = "bool") public boolean bool; @Field(name = "amount") public double amount; @Field(name = "character") public char character; @Field(name = "name") public String name; @Field(name = "timestamp") public Date timestamp; } Controller class; @RestController public class HomeController { @Autowired private DataRepository dataRepository; @GetMapping("/") public ApiResponse index() { List<Test2> testList = new ArrayList<>(); testList.add(new Test2(1,2000, true, 2000.89,"Ibrahim Olanrewaju",new Date())); testList.add(new Test2(2,30000, false, 2000.89,"Adewale Ojo",new Date())); testList.add(new Test2(3,40000, true, 2000.89,"Heemed Sulimon",new Date())); testList.add(new Test2(4,5000, true, 2000.89,"Joe Leo",new Date())); Test test = dataRepository.insert(new Test( 1, 2000, 2000.0, "OLanrewaju Ibrahim", new Test2(4,5000, true, 2000.89,"Joe Leo",new Date()), testList,  new Date() ) ); System.out.println("After Creation TEST Info: "+test); }  creating type: CREATE TYPE quickschoolinfo.test2( id int, number int, bool boolean, amount double, character varchar, name text, timestamp date, ) creating a table; CREATE TABLE quickschoolinfo.test( id int, number int, bool boolean, amount double, character varchar, name text, test2 frozen<test2>, test2List  list<forzen<test2>>, timestamp date, PRIMARY KEY((id, amount), timestamp, name)); Please, can you put me through where I made a mistake or omitting something? Thanks in advance.

Like
Reply

Awesome. Can you please help me with batching of queries in this example. As I have multiple(10k approx) values for where condition. Thanks.

Like
Reply

To view or add a comment, sign in

More articles by Nikunj Pandya

  • Hitchhikers guide to Investing - Mutual Funds

    I started working recently and it’s great getting independent, managing all your aspects of life and what you can do…

  • Future of Machine Learning

    The concept of machine learning is not as recent as we might think, it was coined in late 1960's, neural networks also…

  • Introduction to Machine learning

    Artificial Intelligence vs Machine Learning - What is it all about? In recent times you might have heard these…

    1 Comment

Others also viewed

Explore content categories