Skip to Content
Technical Articles
Author's profile photo Boudhayan Dev

Develop a Spring Boot (Java) application with HANA database on SAP Cloud Platform (Cloud Foundry) – PART 1

In this blog series, we will develop a Spring Boot application and deploy it on SAP Cloud Platform (Cloud Foundry). For persistence, we will make use of SAP HANA service on SCP. The goal of this project is to show how we can make use of HANA database in Cloud foundry. The application itself is very simple – REST endpoints supporting CRUD operations on Employee entity.

The code used in this blog can be found here – GitHub

So, let’s get started.

STEP 1 : Create the Spring boot application.


1.1 Create Spring Boot project using Spring Starter project in Eclipse

Install Spring Tools 4 for Eclipse.

Select New project -> Spring -> Create new Spring Boot project.

Group ID – spring-hana-cloud-foundry
Artifact ID – 
spring-hana-cloud-foundry
Name –
spring- hana-cloud-foundry
Description –
Sample Spring Boot application to use SAP HANA Service

1.2 Add the following dependencies, profiles and plugins in pom.xml

pom.xml –

<?xml version="1.0" encoding="UTF-8"?>
<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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>spring-hana-cloud-foundry</groupId>
	<artifactId>spring-hana-cloud-foundry</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>spring-hana-cloud-foundry</name>
	<description>Sample Spring Boot application to use SAP HANA Service</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
		    <groupId>org.springframework.cloud</groupId>
		    <artifactId>spring-cloud-cloudfoundry-connector</artifactId>
		    <version>1.2.2.RELEASE</version>
		    <scope>runtime</scope>
		</dependency>
		<dependency>
		    <groupId>com.sap.hana.cloud</groupId>
		    <artifactId>spring-cloud-cloudfoundry-hana-service-connector</artifactId>
		    <version>1.0.4.RELEASE</version>
		</dependency>
		<dependency>
		    <groupId>org.springframework.cloud</groupId>
		    <artifactId>spring-cloud-spring-service-connector</artifactId>
		    <version>1.2.2.RELEASE</version>
		</dependency>
		
		<dependency>
		    <groupId>com.sap.db.jdbc</groupId>
		    <artifactId>ngdbc</artifactId>
		    <version>2.3.55</version>
		</dependency>

	</dependencies>
	
	<profiles>
		 <profile>
			<id>local</id>
			<activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
		        <activatedProperties>local</activatedProperties>
		    </properties>
            <dependencies>
            	<dependency>
		            <groupId>com.h2database</groupId>
		            <artifactId>h2</artifactId>
		            <scope>runtime</scope>
		        </dependency>
            </dependencies>
		</profile>
		
		<profile>
			<id>cf</id>
			<activation>
				<activeByDefault>false</activeByDefault>
			</activation>
			<properties>
		        <activatedProperties>cf</activatedProperties>
		    </properties>
		    
		</profile>
	</profiles>
	

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

 

  • spring-boot-starter-data-jpa​ – Starter for using Spring Data JPA with Hibernate
  • lombok – Provides automatic getter, setters and other convenient annotations for your POJOs.
  • h2 – In-memory database used for local testing

The important dependencies are –

  • spring-cloud-cloudfoundry-connector – It simplifies the process of connecting to services in cloud environments like Cloud Foundry.
  • spring-cloud-spring-service-connector – This library provides data source implementations for spring data connector.
  • spring-cloud-cloudfoundry-hana-service-connector – Hana connector for Spring boot.
  • ngdbc – HANA driver.

 

1.3 Create the Models, Repository, Service and Controller for the application

Create model – Employee.java

package com.sap.springhanacloudfoundry.models;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.Getter;
import lombok.AllArgsConstructor;

@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class Employee {
	@Id
	@Column(name ="id", unique=true)
	private long id;
	@Column(name ="firstName")
	private String firstName;
	@Column(name ="lastName")
	private String lastName;
	@Column(name ="email", unique=true)
	private String email;
	@Column(name ="contact",unique=true)
	private String contact;
}

 

Create corresponding repository for the model class – EmployeeRepository.java

package com.sap.springhanacloudfoundry.repository;

import org.springframework.data.repository.CrudRepository;
import com.sap.springhanacloudfoundry.models.Employee;

public interface EmployeeRepository extends CrudRepository<Employee, Long>{
}

Create corresponding service for the repository – EmployeeService.java

package com.sap.springhanacloudfoundry.services;

import java.util.ArrayList;
import java.util.List;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.sap.springhanacloudfoundry.models.Employee;
import com.sap.springhanacloudfoundry.repository.EmployeeRepository;

@Service
public class EmployeeService {

	@Autowired
	private EmployeeRepository employeeRepository;
	
	public long getCount() {
		long count = employeeRepository.count();
		return count;
	}
	
	public List<Employee> findAllEmployee(){
		List<Employee> employee = new ArrayList<>();
		employeeRepository.findAll().forEach(employee::add);
		return employee;
	}
	
	public boolean insertEmployee(Employee employee) {
	 try {
		employeeRepository.save(employee);
		return true;
	 	} 
	 catch (Exception e) {
		return false;
		}
	}
	
	public Employee findEmployeeById(Long id) {
		Employee employee = employeeRepository.findById(id).orElse(null);
		return employee;
	}
	
	public boolean deleteEmployee(long id) {
		Employee employee = employeeRepository.findById(id).orElse(null);
		if(employee!=null) {
			employeeRepository.delete(employee);
			return true;
		}
		return false;
	}
}

 

Create the controller to handle all the requests – EmployeeController.java

package com.sap.springhanacloudfoundry.controller;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.sap.springhanacloudfoundry.models.Employee;
import com.sap.springhanacloudfoundry.services.EmployeeService;

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;

@RestController
public class EmployeeController {
	
	Logger log = LoggerFactory.getLogger(getClass());
	
	@Autowired
	private EmployeeService employeeService;
	
	@RequestMapping("/employee/count")
	public long count() {
		log.info("Search total number of employees");
		return employeeService.getCount();
	}
	
	@RequestMapping("/employee/all")
	public List<Employee> getAllEmployees(){
		log.info("Searching all employees");
		return employeeService.findAllEmployee();
	}
	
	@RequestMapping(method=RequestMethod.POST, value = "/employee/add")
	public boolean addEmployee(@RequestBody Employee employee) {
		
		log.info("Creation/Updating Employee - "+employee.toString());
		return employeeService.insertEmployee(employee);
	}
	
	@RequestMapping("/employee/id/{id}" )
	public Employee findById(@PathVariable long id) {
		log.info("Searching employee with ID - "+ id);
		return employeeService.findEmployeeById(id);
	}
	
	@RequestMapping(method=RequestMethod.DELETE, value="/employee/delete/{id}")
	public boolean deleteEmployee(@PathVariable long id) {
		return employeeService.deleteEmployee(id);
	}
	
}

 

And finally we come to most important bit of code, the configuration of the datasource.

We will bind the datasource to the application on runtime. The datasource details are available as environment variables in CF  also known as – VCAP_SERVICES. At the time of writing this blog, the datasource could not be bound to the application directly. The url, username and password needed to be injected manually into the config class due to an issue with the ngdbc driver – Issue

 

Create a config class for the datasource as follows  –  CloudDatabaseConfig.java

package com.sap.springhanacloudfoundry.config;

import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.cloud.config.java.AbstractCloudConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import com.zaxxer.hikari.HikariDataSource;

@Configuration
@Profile("cloud")
public class CloudDatabaseConfig extends AbstractCloudConfig {
	
	@Bean
	public DataSource dataSource(@Value("${hana.url}")final String url,
			@Value("${hana.user}")final String user,
			@Value("${hana.password}")final String password) {
		
		
		return DataSourceBuilder.create()
				.type(HikariDataSource.class)
				.driverClassName(com.sap.db.jdbc.Driver.class.getName())
				.url(url)
				.username(user)
				.password(password)
				.build();	

	}
}

 

1.4 Create application properties

 

With that we are done with our application logic. The final bit is the application configuration which would define how we connect to the database in cloud as well as in local.

For cloud , create properties file as – application-cf.properties

spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.HANAColumnStoreDialect
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

hana.url = ${vcap.services.hana_migration.credentials.url}
hana.user = ${vcap.services.hana_migration.credentials.user}
hana.password = ${vcap.services.hana_migration.credentials.password}

 

hana_migration is the name of the HANA service instance bound to the application (We will cover this topic in the next part).

And for local testing , create properties file as – application-local.properties

spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy
spring.h2.console.enabled=true

 

And in application.properties add –

spring.profiles.active=@activatedProperties@

 

With that, we are ready with our application code. The next step would be to deploy the application on cloud foundry and test it.

 

Additionally, if you want to run the application in local and test, you can run it as a Spring Boot app from Eclipse. The following endpoints are available for testing –

  1. GET /employee/count -> Returns count of total employees in database.
  2. GET /employee/all -> Returns all employees in database.
  3. GET /employee/id/{id} -> Returns the employee instance corresponding to the ID.
  4. POST /employee/add -> Add a new employee in database. Use the following payload in body –
    {
    	"id":"1",
    	"firstName":"Boudhayan",
    	"lastName":"Dev",
    	"email":"email@example.com",
    	"contact":"12121212"
    }
  5. DELETE /employee/delete/{id} -> Delete particular employee from database.

 

See you in the next part where we configure the HANA instance in Cloud platform.

Cheers.

 

Find the remaining parts here –

  1. Develop the Spring Boot Application (PART 1) – Develop a Spring Boot (Java) application with HANA database on SAP Cloud Platform (Cloud Foundry) – PART 1
  2. Create instance of HANA service (PART 2) – Develop a Spring Boot (Java) application with HANA database on SAP Cloud Platform (Cloud Foundry) – PART 2
  3. Deploy and Test (PART 3) – Develop a Spring Boot (Java) application with HANA database on SAP Cloud Platform (Cloud Foundry) – PART 3

Assigned Tags

      15 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Mark De Vries
      Mark De Vries

      An even easier way to automatically get the credentials wired up is using CFJavaEnv

      Assuming you are using spring boot 2.x

      simply add

      		<dependency>
      			<groupId>io.pivotal.cfenv</groupId>
      			<artifactId>java-cfenv-boot</artifactId>
      			<version>2.1.0.RELEASE</version>
      		</dependency>

      to your pom and add the wiring:

      	@Bean
      	@Primary
      	@Profile("cloud")
      	public DataSourceProperties dataSourceProperties() {
      		CfJdbcEnv cfJdbcEnv = new CfJdbcEnv();
      		DataSourceProperties properties = new DataSourceProperties();
      		CfCredentials hanaCredentials = cfJdbcEnv.findCredentialsByTag("hana");
      
      		if (hanaCredentials != null) {
      
      			String uri = hanaCredentials.getUri("hana");
      			properties.setUrl(uri);
      			properties.setUsername(hanaCredentials.getUsername());
      			properties.setPassword(hanaCredentials.getPassword());
      		}
      
      		return properties;
      	}

      This is enough to wire up the datasource.

      Author's profile photo Vasyl Klindukhov
      Vasyl Klindukhov

      Author's profile photo Boudhayan Dev
      Boudhayan Dev
      Blog Post Author

      Thanks

      This library will be very helpful in parsing CF environment variables. I always wondered why don't we have a parser library in Java when something similar already existed in Node (@SAP/xsenv and cfenv). I guess that problem is solved.
      Regards
      Author's profile photo Abdulwasa Abdulkader Osman
      Abdulwasa Abdulkader Osman

      How looks the application.properties now with this dependecy ?

      Author's profile photo Abdulwasa Abdulkader Osman
      Abdulwasa Abdulkader Osman

      Hi Guys,
      I get this Error on my pom.xml file. Can you help please ?

      I am using this Dependecy:

      <dependency>
      <groupId>com.sap.db.jdbc</groupId>
      <artifactId>ngdbc</artifactId>
      <version>2.3.55</version>
      </dependency>
      </dependencies>

       

      Error: Missing artifact com.sap.db.jdbc:ngdbc:jar:2.3.55

      The Driver is not found: com.sap.db.jdbc.Driver.class.getName()

      Author's profile photo Mathan Selvaraj
      Mathan Selvaraj

      Hello,

      The group Id is different here. In pom.xml make the following change.

      <groupId>com.sap.cloud.db.jdbc</groupId>

       

      Thanks!

       

      Author's profile photo Kayur Goyal
      Kayur Goyal

      Hi,

      I am getting the following error when I try and push the application to Cloud Foundry,

      I am using Cloud Foundry Trial.

      Below is my pom.

      <?xml version="1.0" encoding="UTF-8"?>
      <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>
      	<parent>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-parent</artifactId>
      		<version>2.1.6.RELEASE</version>
      		<relativePath/> <!-- lookup parent from repository -->
      	</parent>
      	<groupId>spring-hana-cloud-foundry</groupId>
      	<artifactId>spring-hana-cloud-foundry</artifactId>
      	<version>0.0.1-SNAPSHOT</version>
      	<packaging>war</packaging>
      	<name>spring-hana-cloud-foundry</name>
      	<description>Sample Spring Boot application to use SAP HANA Service</description>
      
      	<properties>
      		<java.version>1.8</java.version>
      	</properties>
      
      	<dependencies>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-data-jpa</artifactId>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-web</artifactId>
      		</dependency>
      
      		<dependency>
      			<groupId>org.projectlombok</groupId>
      			<artifactId>lombok</artifactId>
      			<optional>true</optional>
      		</dependency>
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-tomcat</artifactId>
      			<scope>provided</scope>
      		</dependency>
      
      	
      
      		<dependency>
      		    <groupId>org.springframework.cloud</groupId>
      		    <artifactId>spring-cloud-cloudfoundry-connector</artifactId>
      		    <version>1.2.2.RELEASE</version>
      		    <scope>runtime</scope>
      		</dependency>
      		<dependency>
      		    <groupId>com.sap.hana.cloud</groupId>
      		    <artifactId>spring-cloud-cloudfoundry-hana-service-connector</artifactId>
      		    <version>1.0.4.RELEASE</version>
      		</dependency>
      		<dependency>
      		    <groupId>org.springframework.cloud</groupId>
      		    <artifactId>spring-cloud-spring-service-connector</artifactId>
      		    <version>1.2.2.RELEASE</version>
      		</dependency>
      		
      		<dependency>
      		    <groupId>com.sap.cloud.db.jdbc</groupId>
      		    <artifactId>ngdbc</artifactId>
      		    <version>2.3.55</version>
      			<scope>system</scope>
          		<systemPath>${project.basedir}/src/main/resources/ngdbc-2.3.55.jar</systemPath>
      		</dependency>
      		<dependency>
      			<groupId>io.pivotal.cfenv</groupId>
      			<artifactId>java-cfenv-boot</artifactId>
      			<version>2.1.0.RELEASE</version>
      		</dependency>
      	</dependencies>
      	
      	<profiles>
      		 <profile>
      			<id>local</id>
      			<activation>
                      <activeByDefault>true</activeByDefault>
                  </activation>
                  <properties>
      		        <activatedProperties>local</activatedProperties>
      		    </properties>
                  <dependencies>
                  	<dependency>
      		            <groupId>com.h2database</groupId>
      		            <artifactId>h2</artifactId>
      		            <scope>runtime</scope>
      		        </dependency>
                  </dependencies>
      		</profile>
      		
      		<profile>
      			<id>cf</id>
      			<activation>
      				<activeByDefault>false</activeByDefault>
      			</activation>
      			<properties>
      		        <activatedProperties>cf</activatedProperties>
      		    </properties>
      		    
      		</profile>
      	</profiles>
      	
      
      	<build>
      		<plugins>
      			<plugin>
      				<groupId>org.springframework.boot</groupId>
      				<artifactId>spring-boot-maven-plugin</artifactId>
      			</plugin>
      		</plugins>
      	</build>
      
      </project>
      
      Author's profile photo Anil k
      Anil k

      Were you able to figure it out?

      Author's profile photo Vijay Gorambekar
      Vijay Gorambekar

      I am getting exactly same error. Are you able resolve this? Thanks -Vijay Gorambekar

      Author's profile photo balajee gandi
      balajee gandi

      i added that

      <groupId>com.sap.cloud.db.jdbc</groupId>

      but i got same error

      Author's profile photo Tan Lei
      Tan Lei

      you can do just like this:

      pom.xml

      <!-- https://mvnrepository.com/artifact/com.sap.cloud.db.jdbc/ngdbc -->
      <dependency>
      <groupId>com.sap.cloud.db.jdbc</groupId>
      <artifactId>ngdbc</artifactId>
      <version>2.9.16</version>
      </dependency>

      and CloudDatabaseConfig.java

      @Configuration
      @Profile("cloud")
      public class CloudDatabaseConfig extends AbstractCloudConfig {
      
        @Bean
        public DataSource dataSource(@Value("${hana.url}") final String url,
            @Value("${hana.user}") final String user,
            @Value("${hana.password}") final String password) {
      
          return DataSourceBuilder.create()
              .type(HikariDataSource.class)
              .driverClassName(com.sap.db.jdbc.Driver.class.getName())
              .url(url)
              .username(user)
              .password(password)
              .build();
        }
      }
      
                    
      Author's profile photo Sampath Ramanujam
      Sampath Ramanujam

      I just found out that, the CloudDatabaseConfig is using profile "cloud"

      @Profile("cloud")
      

       

      But you have activated profile is "cf"

      so if you change, the @Profile annotation to "cf", it will resolve your vcap hana.url
      @Profile("cf")

      Please try this.

      Author's profile photo Lekhak Patil
      Lekhak Patil

      Hi Boudhayan,

       

      DataSourceBuilder.create()
      				.type(HikariDataSource.class)
      				.driverClassName(com.sap.db.jdbc.Driver.class.getName())
      				.url(url)
      				.username(user)
      				.password(password)
      				.build();	

       

      What is the significance of HikariDataSource In data source builder??

       

      Without  HikariDataSource as type connection is working fine for me.

       

      Regards,

      Lekhak Patil

      Author's profile photo Boudhayan Dev
      Boudhayan Dev
      Blog Post Author

      HikariDataSource is the connection pool implementation. You have other alternatives - c3p0, dbcp2, tomcat etc.

      It is optional to specify in the config as far as I know. As by default spring data jpa comes bundled with HikariCP dependency.

      Author's profile photo Lekhak Patil
      Lekhak Patil

      Hi Boudhayan,

       

      During application initialization app is able to connect to Hana schema and container instance with provided url, username and password.

      Now we want to try to connect to Hana schema and container instance during runtime, Rather than application initialization time.

      We are working on the multi-tenant scenario and want to connect to different Hana schema and container instances depending on from which tenant request is coming.

      Any help is appreciated.

       

      Regards,

      Lekhak Patil