Configuring a DataSource Programmatically in Spring Boot

1. Overview

Spring Boot uses an opinionated algorithm to scan for and configure a DataSource. This allows us to easily get a fully-configured DataSource implementation by default.

In addition, Spring Boot automatically configures a lightning-fast connection pool, either HikariCP, Apache Tomcat, or Commons DBCP, in that order, depending on which are on the classpath.

While Spring Boot’s automatic DataSource configuration works very well in most cases, sometimes we’ll need a higher level of control, so we’ll have to set up our own DataSource implementation, hence skipping the automatic configuration process.

In this tutorial, we’ll learn how to configure a DataSource programmatically in Spring Boot

2. The Maven Dependencies

Creating a DataSource implementation programmatically is straightforward overall.

To learn how to accomplish this, we’ll implement a simple repository layer, which will perform CRUD operations on some JPA entities.

Let’s take a look at our demo project’s dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.4.1</version> 
    <scope>runtime</scope> 
</dependency>

As shown above, we’ll use an in-memory H2 database instance to exercise the repository layer. In doing so, we’ll be able to test our programmatically-configured DataSource, without the cost of performing expensive database operations.

In addition, let’s make sure to check the latest version of spring-boot-starter-data-jpa on Maven Central.

3. Configuring a DataSource Programmatically

Now, if we stick with Spring Boot’s automatic DataSource configuration and run our project in its current state, it will work just as expected.

Spring Boot will do all the heavy infrastructure plumbing for us. This includes creating an H2 DataSource implementation, which will be automatically handled by HikariCP, Apache Tomcat, or Commons DBCP, and setting up an in-memory database instance.

Additionally, we won’t even need to create an application.properties file, as Spring Boot will provide some default database settings as well.

As we mentioned before, at times we’ll need a higher level of customization, so we’ll have to programmatically configure our own DataSource implementation.

The simplest way to accomplish this is by defining a DataSource factory method, and placing it within a class annotated with the @Configuration annotation:

@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource getDataSource() {
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.driverClassName("org.h2.Driver");
        dataSourceBuilder.url("jdbc:h2:mem:test");
        dataSourceBuilder.username("SA");
        dataSourceBuilder.password("");
        return dataSourceBuilder.build();
    }
}

In this case, we used the convenient DataSourceBuilder class, a non-fluent version of Joshua Bloch’s builder pattern, to programmatically create our custom DataSource object.

This approach is really nice because the builder makes it easy to configure a DataSource using some common properties. It uses the underlying connection pool as well.

4. Externalizing DataSource Configuration With the application.properties File

Of course, it’s also possible to partially externalize our DataSource configuration. For instance, we could define some basic DataSource properties in our factory method:

@Bean 
public DataSource getDataSource() { 
    DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(); 
    dataSourceBuilder.username("SA"); 
    dataSourceBuilder.password(""); 
    return dataSourceBuilder.build(); 
}

Then we can specify a few additional ones in the application.properties file:

spring.datasource.url=jdbc:h2:mem:test
spring.datasource.driver-class-name=org.h2.Driver

The properties defined in an external source, such as the above application.properties file, or via a class annotated with @ConfigurationProperties, will override the ones defined in the Java API.

It becomes evident that, with this approach, we’ll no longer keep our DataSource configuration settings stored in one single place.

On the other hand, it allows us to keep compile-time and run-time configuration settings nicely separated from each other.

This is really good, as it allows us to easily set a configuration binding point. That way we can include different DataSource settings from other sources, without having to refactor our bean factory methods.

5. Testing the DataSource Configuration

esting our custom DataSource configuration is very simple. The whole process boils down to creating a JPA entity, defining a basic repository interface, and testing the repository layer.

5.1. Creating a JPA Entity

Let’s start by defining our sample JPA entity class, which will model users:

@Entity
@Table(name = "users")
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String name;
    private String email;

    // standard constructors / setters / getters / toString
    
}

5.2. A Simple Repository Layer

Next we need to implement a basic repository layer, which allows us to perform CRUD operations on instances of the User entity class defined above.

Since we’re using Spring Data JPA, we don’t have to create our own DAO implementation from scratch. We simply have to extend the CrudRepository interface to get a working repository implementation:

@Repository
public interface UserRepository extends CrudRepository<User, Long> {}

5.3. Testing the Repository Layer

Lastly, we need to check that our programmatically-configured DataSource is actually working. We can easily accomplish this with an integration test:

@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryIntegrationTest {
    
    @Autowired
    private UserRepository userRepository;
   
    @Test
    public void whenCalledSave_thenCorrectNumberOfUsers() {
        userRepository.save(new User("Bob", "bob@domain.com"));
        List<User> users = (List<User>) userRepository.findAll();
        
        assertThat(users.size()).isEqualTo(1);
    }    
}

The UserRepositoryIntegrationTest class is pretty self-explanatory. It simply exercises two of the repository interface’s CRUD methods to persist and find entities.

Notice that regardless of whether we decide to programmatically configure our DataSource implementation, or split it into a Java config method and the application.properties file, we should always get a working database connection.

5.4. Running the Sample Application

Finally, we can run our demo application using a standard main() method:

@SpringBootApplication
public class Application {

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

    @Bean
    public CommandLineRunner run(UserRepository userRepository) throws Exception {
        return (String[] args) -> {
            User user1 = new User("John", "john@domain.com");
            User user2 = new User("Julie", "julie@domain.com");
            userRepository.save(user1);
            userRepository.save(user2);
            userRepository.findAll().forEach(user -> System.out.println(user);
        };
    }
}

We already tested the repository layer, so we’re sure that our DataSource has been configured successfully. Thus, if we run the sample application, we should see in our console output the list of User entities stored in the database.

6. Conclusion

In this article, we learned how to configure a DataSource implementation programmatically in Spring Boot.

As usual, all the code samples shown in this article are available over on GitHub.

Related posts:

Java Program to Implement Fermat Factorization Algorithm
Biến trong java
Java Program to Implement Skew Heap
Java Program to Check if a Point d lies Inside or Outside a Circle Defined by Points a, b, c in a Pl...
Java Program to do a Depth First Search/Traversal on a graph non-recursively
REST Web service: HTTP Status Code và xử lý ngoại lệ RESTful web service với Jersey 2.x
Spring Cloud Bus
Java Program to Implement LinkedBlockingQueue API
Spring Boot - CORS Support
Java Program to Implement the Edmond’s Algorithm for Maximum Cardinality Matching
Java Program to Perform Addition Operation Using Bitwise Operators
Java Program to Implement Hopcroft Algorithm
Find the Registered Spring Security Filters
A Guide to Spring Cloud Netflix – Hystrix
Logging in Spring Boot
Java Program to Check if a Directed Graph is a Tree or Not Using DFS
Java Program to Implement Quick Hull Algorithm to Find Convex Hull
A Guide to System.exit()
Java Program to Implement Ford–Fulkerson Algorithm
HandlerAdapters in Spring MVC
Adding Shutdown Hooks for JVM Applications
Java Program to Perform Cryptography Using Transposition Technique
Disable Spring Data Auto Configuration
Jackson Exceptions – Problems and Solutions
ETL with Spring Cloud Data Flow
Hướng dẫn Java Design Pattern – Adapter
Java Program to Solve a Matching Problem for a Given Specific Case
Guide to System.gc()
Java Timer
Introduction to Thread Pools in Java
Add Multiple Items to an Java ArrayList
Java Program to Implement the String Search Algorithm for Short Text Sizes