Cassandra Object Mapping

Overview

The Datastax Cassandra Java Driver comes with a nice lightweight object mapper. Its goal is not to shield the developer away from the underlying details of Cassandra, like some other fully fledged ORM libraries, but rather to reduce the amount of boilerplate code needed to perform simple CRUD operations.

Boilerplate

The traditional way to retrieve objects using CQL from Cassandra is as follows:

ResultSet result = session.execute("SELECT * FROM accounts.users WHERE account_id=?", accountId);  
List<User> usersList = new ArrayList<>();  
result.iterator().forEachRemaining(row ->  
usersList.add(new User(row.getInt("id"),  
                       row.getString("account_id"), 
                       row.getString("name"), 
                       row.getDate("creation_date")));

The developer must loop through each Row in the ResultSet and create a new data object (in this case a User) from the columns retrieved in the row. There is a neater way....

Object Mapper

The object mapper will handle the conversion between Cassandra types and custom Java objects for you, as well as generating and executing the queries. To use it you must first annotate the data object with the table it maps to.

@Table(keyspace = "accounts", name = "users")
public class User {  
    private int id;
    @Column( name = "account_id" )
    private int accountId;
    private String name;
    @Column( name = "creation_date" )
    private Date creationDate;

By default fields are mapped to columns of the same name. If they differ then you can use the @Column annotation.

You then just create a Mapper and invoke the CRUD operations (in this case 'get' and 'save') you require. Simple!

import com.datastax.driver.core.Cluster;  
import com.datastax.driver.core.Session;  
import com.datastax.driver.mapping.Mapper;  
import com.datastax.driver.mapping.MappingManager;

public User save(User user){  
    Session session = cluster.connect();
    try {
        Mapper<User> mapper = new MappingManager(session).mapper(User.class);
        User user = mapper.get(userId);
        user.setName("John Doe");
        mapper.save(user);
    }finally {
        session.close();
    }
}
Complex Queries

What's nicer again is you can use the object mapper to perform more complex queries by specifying an accessor interface that is annotated with the query you want to perform.

import com.datastax.driver.mapping.Result;  
import com.datastax.driver.mapping.annotations.Accessor;  
import com.datastax.driver.mapping.annotations.Param;  
import com.datastax.driver.mapping.annotations.Query;

@Accessor
interface UsersAccessor {  
    @Query("SELECT * FROM accounts.users WHERE account_id=:accountId")
    Result<User> getAllByAccount(@Param("accountId") int accountId);
}

A mapping manager then creates an object that implements this interface.

MappingManager manager = new MappingManager(session);  
UsersAccessor accessor = manager.createAccessor(UsersAccessor.class);  

You then just invoke the method on the accessor to give you back a nice list of users!

Result<User> result = accessor.getAllByAccount(accountId);  
Conclusion

The object mapper that comes with the Datastax Cassandra driver does a nice job of speeding up development by handling standard CRUD operations for the developer. The Accessor pattern then gives you a nice structure for encapsulating more involved queries.

Kevin Duggan

Kevin Duggan is Technical Team Lead on Newsweaver’s new Cross-Channel Analytics product. He has 10 years experience delivering enterprise solutions for the Energy, Finance and Pharmaceutical sector

Cork

Subscribe to Newsweaver Technology Blog

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!