Externalize Data source configuration with Spring boot

In my previous post, I talked about how to configure spring boot with different connection pooling in which spring boot was using its auto configuration feature to implement connection pooling.

You might have one question that what if we want to customize the way of spring boot to configure data source and want to configure multiple data source at the same time? This is the post which will do it.

In today’s post we will discuss how to customize or configure your own data source in spring boot.

To customize your own datasource with spring boot following are the steps:

  • We have tell spring boot to exclude auto configuration for datasource by using exclude attribute of @SpringBootApplication annotation as below:
@SpringBootApplication(exclude = { DataSourceTransactionManagerAutoConfiguration.class, DataSourceAutoConfiguration.class })

Note: We can only exclude those classes which are Auto Configurable classes.

  • Define a @Bean of that type in your configuration as below. Spring Boot will reuse your DataSource anywhere one is required, including database initialization. Read More
@Bean
@ConfigurationProperties("app.datasource")
public DataSource dataSource() {
    return DataSourceBuilder.create().build();
}
  • Define a @Bean to read or access application.properties file in order to read DB details to make database connection as below:
import org.springframework.stereotype.*
import org.springframework.beans.factory.annotation.*
import org.springframework.context.annotation.PropertySource;

@Component
@PropertySource("classpath:application.properties")
public class ConfigProperties{

    @Value("${name}")
    private String name;

    // ...

}

In above bean, I have used @PropertySource to read application.properties file. There are multiple ways through which application.properties can be access. To know more about it follow the link.

Let’s create a sample project to customize the way of datasource and make database connection with default connection pooling.

Create Maven project

Create maven based project in eclipse with Archetype web and update pom.xml as below:

<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>springboot</groupId>
	<artifactId>Spring-boot</artifactId>
	<packaging>jar</packaging>
	<version>0.0.1</version>
	<name>Application Maven Webapp</name>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.2.RELEASE</version>
	</parent>

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

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-jdbc</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<!-- DBCP2 -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-dbcp2</artifactId>
		</dependency>

	</dependencies>
	<build>
		<finalName>spring-boot-application</finalName>
	</build>
</project>

In this post, I used MySql database so let’s create following table in your database:

CREATE TABLE `employee` (
  `emp_id` INT,
  `employee_name` VARCHAR(45) NULL,
  `employee_designation` VARCHAR(45) NULL,
  `employee_salary` INT NULL
  PRIMARY KEY (`emp_id`));

And Insert following data into the table as below:

insert into employee values (1, 'Ashish kumar', 'Java developer', '10000');
insert into employee values(2, 'Rahul sharma', 'QA', '20000');
insert into employee values (3, 'Arjun kumar', 'Android developer', '15000');

Create application.properties file and put it under src/main/resources folder as below:

#Property file that defines database details
username=xxxx
password=xxxx
initialSize=7
maxTotal=20
dbName=xxx
dbIp=xxxx
dbPort=xxxx
dbType =mysql

Create following model class for employee:

package com.threadminions.model;

public class Employee {

	private int empId;
	private String employeeName;
	private String employeeDesignation;
	private int employeeSalary;

	public int getEmpId() {
		return empId;
	}
	public void setEmpId(int empId) {
		this.empId = empId;
	}
	public String getEmployeeName() {
		return employeeName;
	}
	public void setEmployeeName(String employeeName) {
		this.employeeName = employeeName;
	}
	public String getEmployeeDesignation() {
		return employeeDesignation;
	}
	public void setEmployeeDesignation(String employeeDesignation) {
		this.employeeDesignation = employeeDesignation;
	}
	public int getEmployeeSalary() {
		return employeeSalary;
	}
	public void setEmployeeSalary(int employeeSalary) {
		this.employeeSalary = employeeSalary;
	}

}

Create Extractor class for parsing result set to employee model using RowMapper interface as below:

package com.threadminions.service;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import com.threadminions.model.Employee;

public class Extractor implements RowMapper<Employee>
{
    @Override
    public Employee mapRow(ResultSet rs, int arg1) throws SQLException
    {
          Employee emp = new Employee();
          emp.setEmpId(rs.getInt("emp_id"));
          emp.setEmployeeName(rs.getString("employee_name"));
              emp.setEmployeeDesignation(rs.getString("employee_designation"));
          emp.setEmployeeSalary(rs.getInt("employee_salary"));

          return emp;
    }
}

Create following classes to print employees and about the connection pooling details:

package com.threadminions.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.threadminions.model.Employee;

@Component
public class EmployeeServiceLayer {

	@Autowired
	JdbcTemplate jdbcTemplate;

	@Scheduled(fixedRate = 5000)
	public void printEmployees()
	{
		List<Employee> allEmps = jdbcTemplate.query("select * from employee", new Extractor());

		allEmps.forEach(emp ->
		{
			System.out.println("Employee Name: " + emp.getEmployeeName());
			System.out.println("Employee Salary: " + emp.getEmployeeSalary());
		});
	}
}

DataBaseRepository class to print connection pooling details:

package com.threadminions.service;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DatabaseRepository {

	@Autowired
	DataSource dataSource;

	/***
	 * If you are using DBCP connection pooling then use this method to get the connection details
	 */
	public void printDBCPConnectionDetails()
	{
		BasicDataSource basicDataSource = (BasicDataSource) dataSource;
		System.out.println("Instace of DBCP basic data source: " + basicDataSource);
		System.out.println("Driver class name: " + basicDataSource.getDriverClassName());
		System.out.println("Max idle connection: " + basicDataSource.getMaxIdle());
		System.out.println("Total connection: " + basicDataSource.getMaxTotal());
	}
}

Create a ConfigProperties class to read application.properties file as below:

package com.threadminions.model;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource("classpath:application.properties")
public class ConfigProperty {

	@Value("${username}")
	private String username;

	@Value("${password}")
	private String password;

	@Value("${maxTotal}")
	private int maxTotal;

	@Value("${initialSize}")
	private int initialSize;

	@Value("${dbType}")
	private String dbType;

	@Value("${dbName}")
	private String dbName;

	@Value("${dbIp}")
	private String dbIp;

	@Value("${dbPort}")
	private String dbPort;

	public String getDbType() {
		return dbType;
	}

	public void setDbType(String dbType) {
		this.dbType = dbType;
	}

	public String getDbName() {
		return dbName;
	}

	public void setDbName(String dbName) {
		this.dbName = dbName;
	}

	public String getDbIp() {
		return dbIp;
	}

	public void setDbIp(String dbIp) {
		this.dbIp = dbIp;
	}

	public String getDbPort() {
		return dbPort;
	}

	public void setDbPort(String dbPort) {
		this.dbPort = dbPort;
	}

	public int getMaxTotal() {
		return maxTotal;
	}

	public void setMaxTotal(int maxTotal) {
		this.maxTotal = maxTotal;
	}

	public int getInitialSize() {
		return initialSize;
	}

	public void setInitialSize(int initialSize) {
		this.initialSize = initialSize;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}

Now, finally create a configuration class SpringBootConfig in which we will define a @Bean to create a datasource and make connection with database as below:

package com.threadminions;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.context.annotation.Bean;

import com.threadminions.model.ConfigProperty;
import com.threadminions.service.DatabaseRepository;
import com.threadminions.service.EmployeeServiceLayer;

@SpringBootApplication(exclude = { DataSourceTransactionManagerAutoConfiguration.class, DataSourceAutoConfiguration.class })
public class SpringBootConfig implements CommandLineRunner
{
	@Autowired
	EmployeeServiceLayer employeeService;

	@Autowired
	DatabaseRepository databaseRepository;

	@Bean(destroyMethod = "")
	public DataSource dataSource(ConfigProperty configProperty)
	{
		String url = "";
		String password = configProperty.getPassword();
		String dbType = configProperty.getDbType();
		String driverClassName = "";
		if ("mysql".equalsIgnoreCase(dbType)) {
			driverClassName = "com.mysql.jdbc.Driver";
			url = "jdbc:mysql://" + configProperty.getDbIp() + ":" + configProperty.getDbPort() + "/"
					+ configProperty.getDbName();
		}

		else if ("oracle".equalsIgnoreCase(dbType)) {
			driverClassName = "oracle.jdbc.driver.OracleDriver";
			url = "jdbc:oracle:thin:@" + configProperty.getDbIp() + ":" + configProperty.getDbPort() + ":"
					+ configProperty.getDbName();
		}

		return DataSourceBuilder.create().driverClassName(driverClassName).url(url).username("root").password(password)
				.type(BasicDataSource.class).build();
	}

	public static void main(String[] ar) {
		SpringApplication.run(SpringBootConfig.class, ar);
	}

	@Override
	public void run(String... arg0) throws Exception {
		employeeService.printEmployees();
		databaseRepository.printDBCPConnectionDetails();
	}
}

In this class, I have defined datasource bean which is using DataSourceBuilder to create datasource object and on the basis of dbType it is making connection with database.

All things has been set. Now, its time to run the program so run it as java application and it will show you following results:

spring_boot_with_externalize_datasource

Click here for source code…..!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s