Backend Development 10 min read

Master Custom Data Sources and JPA Configuration in Spring Boot

This guide walks through configuring custom and multiple data sources, using Spring Data repositories, fine‑tuning JPA and Hibernate settings, customizing naming strategies, enabling second‑level caching, and exposing repositories as REST endpoints in a Spring Boot 2.4.13 application.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Custom Data Sources and JPA Configuration in Spring Boot

Environment: Spring Boot 2.4.13

Custom DataSource Configuration

<code>@Bean
@ConfigurationProperties(prefix="app.datasource")
public DataSource dataSource() {
    return new FancyDataSource();
}
</code>

Corresponding YAML configuration:

<code>app:
  datasource:
    url: "jdbc:h2:mem:mydb"
    username: "sa"
    password: "123123"
    pool-size: 30
</code>

The FancyDataSource class provides url , username , and pool-size properties.

Using DataSourceBuilder

<code>@Bean
@ConfigurationProperties("app.datasource")
public DataSource dataSource() {
    return DataSourceBuilder.create().build();
}
</code>

Be aware of a trap: if the actual pool type is not supplied, no metadata keys are generated and IDE completion may be missing. When HikariCP is on the classpath, the basic setup fails because Hikari uses jdbcUrl instead of url . In that case use:

<code>app:
  datasource:
    jdbc-url: "jdbc:mysql://localhost/test"
    username: "dbuser"
    password: "dbpass"
    pool-size: 30
</code>

Force a Specific DataSource Type

<code>@Bean
@ConfigurationProperties("app.datasource")
public HikariDataSource dataSource() {
    return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
</code>

Multiple DataSource Configuration

When configuring more than one data source, mark one as @Primary so that auto‑configuration can locate a default.

<code>@Bean
@Primary
@ConfigurationProperties("app.datasource.first")
public DataSourceProperties firstDataSourceProperties() {
    return new DataSourceProperties();
}

@Bean
@Primary
@ConfigurationProperties("app.datasource.first.configuration")
public HikariDataSource firstDataSource() {
    return firstDataSourceProperties()
        .initializeDataSourceBuilder()
        .type(HikariDataSource.class)
        .build();
}

@Bean
@ConfigurationProperties("app.datasource.second")
public BasicDataSource secondDataSource() {
    return DataSourceBuilder.create().type(BasicDataSource.class).build();
}
</code>

YAML example for the two data sources:

<code>app:
  datasource:
    first:
      url: "jdbc:mysql://localhost/first"
      username: "dbuser"
      password: "dbpass"
      configuration:
        maximum-pool-size: 30
    second:
      url: "jdbc:mysql://localhost/second"
      username: "dbuser"
      password: "dbpass"
      max-total: 30
</code>

Using Spring Data Repositories

Spring Data can generate implementations for @Repository interfaces automatically when they reside in the same package (or sub‑package) as the @EnableAutoConfiguration class. Add the appropriate starter, e.g., spring-boot-starter-data-jpa or spring-boot-starter-data-mongodb , and create repository interfaces for your @Entity classes.

Separate @Entity Scanning from Spring Configuration

<code>@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = City.class)
public class Application {
    // ...
}
</code>

Configure JPA Properties

Spring Data JPA exposes vendor‑independent options such as SQL logging. The spring.jpa.hibernate.ddl-auto property defaults to create-drop for embedded databases without a schema manager, otherwise to none . To set a specific dialect, use spring.jpa.database-platform .

<code>spring:
  jpa:
    hibernate:
      naming:
        physical-strategy: "com.example.MyPhysicalNamingStrategy"
    show-sql: true
</code>

Hibernate Naming Strategy

Hibernate maps object names to database identifiers using physical and implicit strategies. The default in Spring Boot is SpringPhysicalNamingStrategy , which converts camel case to snake case and lower‑cases table names. To use a case‑sensitive strategy, define a bean:

<code>@Bean
public SpringPhysicalNamingStrategy caseSensitivePhysicalNamingStrategy() {
    return new SpringPhysicalNamingStrategy() {
        @Override
        protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
            return false;
        }
    };
}
</code>

Alternatively, switch to Hibernate’s standard implementation:

<code>spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
</code>

Hibernate Second‑Level Cache

To enable a second‑level cache with JCache, ensure org.hibernate.HibernateJCache is on the classpath and provide a HibernatePropertiesCustomizer bean:

<code>@Configuration(proxyBeanMethods = false)
public class HibernateSecondLevelCacheExample {
    @Bean
    public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager cacheManager) {
        return properties -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager());
    }
}
</code>

Multiple EntityManagerFactories

When several data sources require separate JPA contexts, define a distinct EntityManagerFactory for each. Use LocalContainerEntityManagerFactoryBean and share common JPA properties:

<code>@Bean
@ConfigurationProperties("app.jpa.first")
public JpaProperties firstJpaProperties() {
    return new JpaProperties();
}

@Bean
public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(DataSource firstDataSource, JpaProperties firstJpaProperties) {
    EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(firstJpaProperties);
    return builder
        .dataSource(firstDataSource)
        .packages(Order.class)
        .persistenceUnit("firstDs")
        .build();
}

private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {
    JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
    return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);
}

private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
    // Map JPA properties as needed
    return new HibernateJpaVendorAdapter();
}
</code>

Expose Spring Data Repositories as REST Endpoints

Include the Spring Data REST starter to automatically publish repository interfaces as RESTful services (requires Spring MVC):

<code>&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-data-rest&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.data&lt;/groupId&gt;
    &lt;artifactId&gt;spring-data-rest-webmvc&lt;/artifactId&gt;
&lt;/dependency&gt;
</code>

With these configurations, you can fully customize data source handling, JPA/Hibernate behavior, caching, and expose data access layers as REST APIs in a Spring Boot backend.

Spring BootSpring DataDataSourceHibernateJPA
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.