Thursday, August 20, 2015

RESTful API using Spring Boot and Cassandra

In this post, we are going to learn how to implement RESTful web services using Spring Boot and Cassandra db.

Project Structure:

Tools & Technologies:
  • Java 8
  • Maven 3.0.3
  • Spring Boot 1.2.5.RELEASE
  • Cassandra Database 2.1.8
  • Spring data cassandra 1.2.2.RELEASE
Cassandra Keyspace and Table Creation:
cqlsh> create keyspace "ranga" with replication = {'class': 'SimpleStrategy', 'replication_factor': '1'};
cqlsh:system_traces> use ranga;
cqlsh:ranga> CREATE TABLE employee(id bigint PRIMARY KEY, name text, age int, salary float);
cqlsh:system_traces> INSERT INTO employee(id, name, age, salary) VALUES(1, 'Ranga', 27, 30000);
cqlsh:ranga> select * from employee;

id | age | name | salary
----+-----+-------+--------
1 | 27 | Ranga | 30000
Maven Configuration
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<groupId>com.ranga</groupId>
<artifactId>SpringBootRestAPICassandraExample</artifactId>
<version>1.0.0-SNAPSHOT</version>

<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
</parent>

<dependencies>
<!-- Get the dependencies of a web application -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Data Cassandra -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-cassandra</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

</project>

Configuring the Cassandra DB properties
src/main/resources/cassandra.properties
cassandra.contactpoints=localhost,127.0.0.1
cassandra.port=9042
cassandra.keyspace=ranga
Utility class for Cassandra db operations:
CassandraUtil.java
package com.ranga.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.cassandra.config.CassandraClusterFactoryBean;
import org.springframework.data.cassandra.config.CassandraSessionFactoryBean;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;

/**
* Utility class for getting the CassandraOperations object.
* @author Ranga Reddy
* @version 1.0
*/


@Configuration
@PropertySource(value = { "classpath:cassandra.properties" })
public class CassandraUtil {

/**
* Constant String for Keyspace
*/

private static final String KEYSPACE = "cassandra.keyspace";
/**
* Constant String for ContactPoints
*/

private static final String CONTACTPOINTS = "cassandra.contactpoints";
/**
* Constant String for Port
*/

private static final String PORT = "cassandra.port";

@Autowired
private Environment environment;

public CassandraUtil() {
System.out.println("CassandraUtil()");
}

private String getKeyspaceName() {
return environment.getProperty(KEYSPACE);
}

private String getContactPoints() {
return environment
.getProperty(CONTACTPOINTS);
}

private int getPortNumber() {
return Integer.parseInt(environment
.getProperty(PORT));
}

@Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints(getContactPoints());
cluster.setPort(getPortNumber());
return cluster;
}

@Bean
public CassandraMappingContext mappingContext() {
return new BasicCassandraMappingContext();
}

@Bean
public CassandraConverter converter() {
return new MappingCassandraConverter(mappingContext());
}

@Bean
public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean cassandraSessionFactoryBean = new CassandraSessionFactoryBean();
cassandraSessionFactoryBean.setCluster(cluster().getObject());
cassandraSessionFactoryBean.setKeyspaceName(getKeyspaceName());
cassandraSessionFactoryBean.setConverter(converter());
cassandraSessionFactoryBean.setSchemaAction(SchemaAction.NONE);
return cassandraSessionFactoryBean;
}

@Bean
public CassandraOperations cassandraTemplate() throws Exception {
return new CassandraTemplate(session().getObject());
}
}
MyCassandraTemplate.java
package com.ranga.util;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.stereotype.Repository;

/**
* Utility class for handling all CRUD Operations.
* @author Ranga Reddy
* @version 1.0
*/

@Repository
public class MyCassandraTemplate {

@Autowired
private CassandraOperations cassandraTemplate;

/** MyCassandraTemplate Default constructor*/
public MyCassandraTemplate() {
System.out.println("MyCassandraTemplate()");
}

/**
* Creating the entity.
* @param entity
* @return {@link Object}
*/

public <T> T create(T entity) {
return cassandraTemplate.insert(entity);
}

/**
* Creating the list of entities.
* @param entity
*/

public <T> void createList(List<T> entities) {
cassandraTemplate.insert(entities);
}

/**
* Updating the entity.
* @param entity
* @param claz
* @return T
*/

public <T> T update(T entity) {
return (T) cassandraTemplate.update(entity);
}

/**
* Updating the list of entities.
* @param entity
* @param claz
* @return T
*/

public <T> void updateList(List<T> entities) {
cassandraTemplate.update(entities);
}

/**
* Updating the entity.
* @param entity
* @param claz
* @return T
*/

public <T> T update(T entity, Class<T> claz) {
return (T) cassandraTemplate.update(entity);
}

/**
* Get the Entity using Id.
* @param id
* @param claz
* @return T
*/

public <T> T findById(Object id, Class<T> claz) {
return cassandraTemplate.selectOneById(claz, id);
}

/**
* Delete the Entity using Id.
* @param id
* @param claz
*/

public <T> void deleteById(Object id, Class<T> claz) {
cassandraTemplate.deleteById(claz, id);
}

/**
* Delete the Entity using object.
* @param entity
*/

public void delete(Object entity) {
cassandraTemplate.delete(entity);
}

/**
* Deleting the list of entities
* @param entities
*/

public <T> void delete(List<T> entities) {
cassandraTemplate.delete(entities);
}

/**
* Deleting the all entities.
* @param claz
*/

public <T> void deleteAll(Class<T> claz) {
cassandraTemplate.deleteAll(claz);
}

/**
* Getting the all entities.
* @param claz
* @return List of entities
*/

public <T> List<T> findAll(Class<T> claz) {
return (List<T>) cassandraTemplate.selectAll(claz);
}

/**
* Getting the all entity values using specific id's data.
* @param ids
* @param claz
* @return
*/

public <T> List<T> findAll(List<Object> ids, Class<T> claz) {
return cassandraTemplate.selectBySimpleIds(claz, ids);
}

/**
* Getting the count of records.
* @param claz
* @return the count value.
*/

public <T> void truncate(Class<T> claz) {
cassandraTemplate.truncate(claz.getName());
}

/**
* Getting the count of records.
* @param claz
* @return the count value.
*/

public <T> long getCount(Class<T> claz) {
return cassandraTemplate.count(claz);
}


/**
* Checking the object exists or not.
* @param id
* @param claz
* @return true if the object exists in the database otherwise it will return false.
*/

public <T> boolean exists(Object id, Class<T> claz) {
return cassandraTemplate.exists(claz, id);
}

}
Employee entity:
Employee.java
package com.ranga.entity;

import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table;

/**
* Employee entity class.
*
* @author Ranga Reddy
* @version 1.0
*/

@Table("employee")
public class Employee {

@PrimaryKey("id")
private long id;

@Column("name")
private String name;

@Column
private int age;

@Column(value ="salary")
private float salary;

/**
* Default Constructor
*/

public Employee() {
super();
}

/**
* Parameterized Constructor
* @param id
* @param name
* @param age
* @param salary
*/

public Employee(long id, String name, int age, float salary) {
super();
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}

/**
* @return the id
*/

public long getId() {
return id;
}

/**
* @param id the id to set
*/

public void setId(long id) {
this.id = id;
}

/**
* @return the name
*/

public String getName() {
return name;
}

/**
* @param name the name to set
*/

public void setName(String name) {
this.name = name;
}

/**
* @return the age
*/

public int getAge() {
return age;
}

/**
* @param age the age to set
*/

public void setAge(int age) {
this.age = age;
}

/**
* @return the salary
*/

public float getSalary() {
return salary;
}

/**
* @param salary the salary to set
*/

public void setSalary(float salary) {
this.salary = salary;
}

/* (non-Javadoc)
* @see java.lang.Object#toString()
*/

@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age
+ ", salary=" + salary + "]";
}
}
Employee DAO & Impl classes;
EmployeeDAO.java
package com.ranga.dao;

import java.util.List;

import com.ranga.entity.Employee;

/**
* DAO interface for Employee to perform CRUD operation.
* @author Ranga Reddy
* @version 1.0
*/

public interface EmployeeDAO {
/**
* Used to Create the Employee Information
* @param employee
* @return {@link Employee}
*/

public Employee createEmployee(Employee employee);

/**
* Getting the Employee Information using Id
* @param id
* @return {@link Employee}
*/

public Employee getEmployee(int id);

/**
* Used to Update the Employee Information
* @param employee
* @return {@link Employee}
*/


public Employee updateEmployee(Employee employee);

/**
* Deleting the Employee Information using Id
* @param id
*/

public void deleteEmployee(int id);

/**
* Getting the All Employees information
* @return
*/

public List<Employee> getAllEmployees();
}
EmployeeDAOImpl.java
/**
*
*/

package com.ranga.dao.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.ranga.dao.EmployeeDAO;
import com.ranga.entity.Employee;
import com.ranga.util.MyCassandraTemplate;

/**
* DAOImpl class for Employee to perform CRUD operation.
* @author Ranga Reddy
* @version 1.0
*/

@Repository
public class EmployeeDAOImpl implements EmployeeDAO {

@Autowired
private MyCassandraTemplate myCassandraTemplate;

@Override
public Employee createEmployee(Employee employee) {
return myCassandraTemplate.create(employee);
}

@Override
public Employee getEmployee(int id) {
return myCassandraTemplate.findById(id, Employee.class);
}

@Override
public Employee updateEmployee(Employee employee) {
return myCassandraTemplate.update(employee, Employee.class);
}

@Override
public void deleteEmployee(int id) {
myCassandraTemplate.deleteById(id, Employee.class);
}

@Override
public List<Employee> getAllEmployees() {
return myCassandraTemplate.findAll(Employee.class);
}
}
Employee Service & Impl classes
EmployeeService.java
package com.ranga.service;

import java.util.List;

import com.ranga.entity.Employee;

/**
* Service interface for Employee to perform CRUD operation.
* @author Ranga Reddy
* @version 1.0
*/

public interface EmployeeService {
/**
* Used to Create the Employee Information
* @param employee
* @return {@link Employee}
*/

public Employee createEmployee(Employee employee);

/**
* Getting the Employee Information using Id
* @param id
* @return {@link Employee}
*/

public Employee getEmployee(int id);

/**
* Used to Update the Employee Information
* @param employee
* @return {@link Employee}
*/


public Employee updateEmployee(Employee employee);

/**
* Deleting the Employee Information using Id
* @param id
*/

public void deleteEmployee(int id);

/**
* Getting the All Employees information
* @return
*/

public List<Employee> getAllEmployees();
}
EmployeeServiceImpl.java
package com.ranga.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ranga.dao.EmployeeDAO;
import com.ranga.entity.Employee;
import com.ranga.service.EmployeeService;

/**
* Service Impl class for Employee to perform CRUD operation.
* @author Ranga Reddy
* @version 1.0
*/

@Service
public class EmployeeServiceImpl implements EmployeeService {

@Autowired
private EmployeeDAO employeeDAO;

/**
* Default Constructor
*/

public EmployeeServiceImpl() {
super();
}

@Override
public Employee createEmployee(Employee employee) {
return employeeDAO.createEmployee(employee);
}

@Override
public Employee getEmployee(int id) {
return employeeDAO.getEmployee(id);
}

@Override
public Employee updateEmployee(Employee employee) {
return employeeDAO.updateEmployee(employee);
}

@Override
public void deleteEmployee(int id) {
employeeDAO.deleteEmployee(id);
}

@Override
public List<Employee> getAllEmployees() {
return employeeDAO.getAllEmployees();
}

}
Employee Rest Controller class:
EmployeeController.java
package com.ranga.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.ranga.entity.Employee;
import com.ranga.service.EmployeeService;

/**
* @author Ranga Reddy
* @version 1.0
* @since Aug 20, 2015
*/

@RestController
public class EmployeeController {

@Autowired
private EmployeeService employeeService;

public EmployeeController() {
System.out.println("EmployeeController()");
}

@RequestMapping(value = "/employee", method = RequestMethod.POST)
Employee create(@RequestBody Employee employee) {
return employeeService.createEmployee(employee);
}

@RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE)
void delete(@PathVariable("id") int id) {
employeeService.deleteEmployee(id);
}

@RequestMapping(value="/employee", method = RequestMethod.GET)
List<Employee> findAll() {
return employeeService.getAllEmployees();
}

@RequestMapping(value = "/employee/{id}", method = RequestMethod.GET)
Employee findById(@PathVariable("id") int id) {
return employeeService.getEmployee(id);
}

@RequestMapping(value = "/employee", method = RequestMethod.PUT)
Employee update(@RequestBody Employee employee) {
return employeeService.updateEmployee(employee);
}
}
Spring Boot Application class:
Application.java
package com.ranga.main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

/**
* Main application to run boot application.
*
* @author Ranga Reddy
* @version 1.0
*/

@SpringBootApplication
@ComponentScan(basePackages="com.ranga")
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
Run the Application.java file.
For testing purpose, i used SOAP UI

Reading All:

Creating:

Reading Specific:

Updating:

Deleting:

5 comments:

bujji said...

Very nice post, Ranga Reddy!

I have similar requirement with the spring-boot and cassandra. I am trying to build an application with the following requirement...

1. A data model to maintain inventory supply and demand records.
2. RESTful API, which reads supply, demand and calculates inventory supply and demand.

How can I build the data model for this scenario? Can you please help me in this regard?

Thanks in advance!!

lab4 said...

You are the best!!!

Unknown said...

Very nice article for beginners.

Unknown said...

It is giving me a whole list of errors with this as the common factor:


nested exception is com.datastax.driver.core.exceptions.NoHostAvailableException: All host(s) tried for query failed (tried: /54.200.224.46:9042 (com.datastax.driver.core.ConnectionException: [/54.200.224.46:9042] Unexpected error during transport initialization (com.datastax.driver.core.TransportException: [/54.200.224.46:9042] Connection has been closed)))

Any idea what is wrong?

amit said...

Thanks for sharing Cassandra details are very nice & you are deeply info about API using Spring Boot and Cassandra related much better.