or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/maven-org-springframework-boot--spring-boot-starter-data-jpa

Starter for using Spring Data JPA with Hibernate

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/org.springframework.boot/spring-boot-starter-data-jpa@3.5.x

To install, run

npx @tessl/cli install tessl/maven-org-springframework-boot--spring-boot-starter-data-jpa@3.5.0

index.mddocs/

Spring Boot Starter Data JPA

Spring Boot Starter Data JPA provides comprehensive auto-configuration for integrating Spring Data JPA with Hibernate ORM in Spring Boot applications. This starter automatically configures JPA EntityManagerFactory, transaction management, repository support, and Hibernate-specific settings, enabling developers to build data access layers with minimal configuration.

Package Information

  • Package Name: spring-boot-starter-data-jpa
  • Package Type: maven
  • Maven Coordinates: org.springframework.boot:spring-boot-starter-data-jpa
  • Language: Java
  • Framework: Spring Boot 3.5.0
  • Installation: Add dependency to pom.xml or build.gradle

Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>3.5.0</version>
</dependency>

Gradle:

implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.5.0'

Core Imports

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.boot.autoconfigure.orm.jpa.*;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import jakarta.persistence.*;

Basic Usage

// 1. Define an entity
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    private String email;

    // Getters and setters
}

// 2. Create a repository interface (Spring Data JPA auto-implements it)
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByName(String name);
    Optional<User> findByEmail(String email);
}

// 3. Use the repository in a service
@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User createUser(String name, String email) {
        User user = new User();
        user.setName(name);
        user.setEmail(email);
        return userRepository.save(user);
    }

    public List<User> findUsersByName(String name) {
        return userRepository.findByName(name);
    }
}

// 4. Configure database connection in application.properties
// spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
// spring.datasource.username=dbuser
// spring.datasource.password=dbpass
// spring.jpa.hibernate.ddl-auto=update
// spring.jpa.show-sql=true

Architecture

The starter provides several layers of auto-configuration:

  • Auto-Configuration Layer: HibernateJpaAutoConfiguration and JpaRepositoriesAutoConfiguration automatically detect dependencies and configure beans
  • JPA Configuration: JpaBaseConfiguration provides EntityManagerFactory, TransactionManager, and related infrastructure beans
  • Repository Support: Spring Data JPA repositories are automatically enabled and implemented
  • Hibernate Integration: Hibernate-specific configuration including naming strategies, DDL handling, and JTA platform
  • Customization Layer: Extensible through properties, customizer interfaces, and bean overrides

Capabilities

Auto-Configuration

The starter automatically configures JPA and Hibernate when dependencies are detected on the classpath.

Auto-Configuration Classes:

@AutoConfiguration(
    after = { DataSourceAutoConfiguration.class, TransactionManagerCustomizationAutoConfiguration.class },
    before = { TransactionAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class }
)
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class, SessionImplementor.class })
@EnableConfigurationProperties(JpaProperties.class)
@Import(HibernateJpaConfiguration.class)
public class HibernateJpaAutoConfiguration {
    // Auto-configures Hibernate as the JPA provider
    // Imports HibernateJpaConfiguration
}

/**
 * Base configuration for JPA. Provides EntityManagerFactory, TransactionManager,
 * and OpenEntityManagerInView support.
 * Package: org.springframework.boot.autoconfigure.orm.jpa
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(JpaProperties.class)
public abstract class JpaBaseConfiguration {
    /**
     * Create a JpaBaseConfiguration instance.
     *
     * @param dataSource The DataSource to use
     * @param properties JPA configuration properties
     * @param jtaTransactionManager Optional JTA transaction manager
     */
    protected JpaBaseConfiguration(
        DataSource dataSource,
        JpaProperties properties,
        ObjectProvider<JtaTransactionManager> jtaTransactionManager
    ) { }

    /**
     * Configure the JPA transaction manager.
     *
     * @param transactionManagerCustomizers Optional customizers
     * @return The configured PlatformTransactionManager
     */
    @Bean
    @ConditionalOnMissingBean(TransactionManager.class)
    public PlatformTransactionManager transactionManager(
        ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers
    ) { }

    /**
     * Create the primary EntityManagerFactory bean.
     *
     * @param factoryBuilder EntityManagerFactoryBuilder for creating the factory
     * @param persistenceManagedTypes Auto-scanned entity types
     * @return LocalContainerEntityManagerFactoryBean
     */
    @Bean
    @Primary
    @ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class, EntityManagerFactory.class })
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
        EntityManagerFactoryBuilder factoryBuilder,
        PersistenceManagedTypes persistenceManagedTypes
    ) { }

    /**
     * Create an EntityManagerFactoryBuilder bean.
     *
     * @param jpaVendorAdapter JPA vendor adapter
     * @param persistenceUnitManager Optional persistence unit manager
     * @param customizers Customizers for the builder
     * @return EntityManagerFactoryBuilder instance
     */
    @Bean
    @ConditionalOnMissingBean
    public EntityManagerFactoryBuilder entityManagerFactoryBuilder(
        JpaVendorAdapter jpaVendorAdapter,
        ObjectProvider<PersistenceUnitManager> persistenceUnitManager,
        ObjectProvider<EntityManagerFactoryBuilderCustomizer> customizers
    ) { }

    /**
     * Return the vendor-specific properties for the given DataSource.
     *
     * @param dataSource The data source
     * @return Map of vendor-specific properties
     * @since 3.4.4
     */
    protected abstract Map<String, Object> getVendorProperties(DataSource dataSource);

    /**
     * Return the vendor-specific properties.
     *
     * @return Map of vendor-specific properties
     * @deprecated since 3.4.4 for removal in 4.0.0 in favor of getVendorProperties(DataSource)
     */
    @Deprecated(since = "3.4.4", forRemoval = true)
    protected Map<String, Object> getVendorProperties() { }

    /**
     * Customize vendor properties before they are used. Allows for post-processing
     * (for example to configure JTA-specific settings).
     *
     * @param vendorProperties The vendor properties to customize
     */
    protected void customizeVendorProperties(Map<String, Object> vendorProperties) { }

    /**
     * Return the JTA transaction manager.
     *
     * @return The transaction manager or null if not using JTA
     */
    protected JtaTransactionManager getJtaTransactionManager() { }

    /**
     * Returns if a JTA PlatformTransactionManager is being used.
     *
     * @return true if a JTA transaction manager is being used
     */
    protected final boolean isJta() { }

    /**
     * Create and configure the JPA vendor adapter bean.
     * Configures the adapter with showSql, database, databasePlatform, and generateDdl settings.
     *
     * @return Configured JPA vendor adapter
     */
    @Bean
    @ConditionalOnMissingBean
    public JpaVendorAdapter jpaVendorAdapter() { }

    /**
     * Return the JpaProperties.
     *
     * @return The JPA properties
     */
    protected final JpaProperties getProperties() { }

    /**
     * Return the DataSource.
     *
     * @return The data source
     */
    protected final DataSource getDataSource() { }

    // Abstract methods that must be implemented by subclasses
    protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter();

    /**
     * Configuration for PersistenceManagedTypes bean.
     * Automatically scans for JPA entities in specified packages.
     * Package: org.springframework.boot.autoconfigure.orm.jpa
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class, EntityManagerFactory.class })
    static class PersistenceManagedTypesConfiguration {
        /**
         * Create PersistenceManagedTypes bean by scanning for entity classes.
         * Scans packages from @EntityScan or auto-configuration packages.
         *
         * @param beanFactory Bean factory for accessing configuration
         * @param resourceLoader Resource loader for scanning
         * @param managedClassNameFilter Optional filter for entity class names
         * @return PersistenceManagedTypes containing discovered entity types
         */
        @Bean
        @Primary
        @ConditionalOnMissingBean
        static PersistenceManagedTypes persistenceManagedTypes(
            BeanFactory beanFactory,
            ResourceLoader resourceLoader,
            ObjectProvider<ManagedClassNameFilter> managedClassNameFilter
        ) { }
    }

    /**
     * Configuration for OpenEntityManagerInView pattern support in web applications.
     * Registers interceptor to bind JPA EntityManager to request thread.
     * Package: org.springframework.boot.autoconfigure.orm.jpa
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass(WebMvcConfigurer.class)
    @ConditionalOnMissingBean({ OpenEntityManagerInViewInterceptor.class, OpenEntityManagerInViewFilter.class })
    @ConditionalOnMissingFilterBean(OpenEntityManagerInViewFilter.class)
    @ConditionalOnBooleanProperty(name = "spring.jpa.open-in-view", matchIfMissing = true)
    protected static class JpaWebConfiguration {
        /**
         * Create JpaWebConfiguration instance.
         *
         * @param jpaProperties JPA configuration properties
         */
        protected JpaWebConfiguration(JpaProperties jpaProperties) { }

        /**
         * Create OpenEntityManagerInView interceptor bean.
         * Logs warning if spring.jpa.open-in-view is not explicitly configured.
         *
         * @return OpenEntityManagerInViewInterceptor instance
         */
        @Bean
        public OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor() { }

        /**
         * Create WebMvcConfigurer to register the OpenEntityManagerInView interceptor.
         *
         * @param interceptor The interceptor to register
         * @return WebMvcConfigurer that registers the interceptor
         */
        @Bean
        public WebMvcConfigurer openEntityManagerInViewInterceptorConfigurer(
            OpenEntityManagerInViewInterceptor interceptor
        ) { }
    }
}

/**
 * Auto-configuration for Spring Data JPA repositories.
 * Package: org.springframework.boot.autoconfigure.data.jpa
 */
@AutoConfiguration(after = { HibernateJpaAutoConfiguration.class, TaskExecutionAutoConfiguration.class })
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class })
@ConditionalOnBooleanProperty(name = "spring.data.jpa.repositories.enabled", matchIfMissing = true)
@Import(JpaRepositoriesImportSelector.class)
public class JpaRepositoriesAutoConfiguration {
    /**
     * Configure EntityManagerFactory bootstrap executor for deferred/lazy initialization.
     *
     * @param taskExecutors Available async task executors
     * @return EntityManagerFactoryBuilderCustomizer
     */
    @Bean
    @Conditional(BootstrapExecutorCondition.class)
    public EntityManagerFactoryBuilderCustomizer entityManagerFactoryBootstrapExecutorCustomizer(
        Map<String, AsyncTaskExecutor> taskExecutors
    ) { }

    /**
     * Import selector that chooses the appropriate repository registrar.
     * Selects EnversRevisionRepositoriesRegistrar if Hibernate Envers is on the classpath,
     * otherwise selects JpaRepositoriesRegistrar.
     */
    static class JpaRepositoriesImportSelector implements ImportSelector {
        /**
         * Select which repository configuration class to import.
         * Returns EnversRevisionRepositoriesRegistrar if Envers is available,
         * otherwise returns JpaRepositoriesRegistrar.
         *
         * @param importingClassMetadata Metadata of the importing class
         * @return Array containing the fully qualified name of the registrar class to import
         */
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) { }
    }
}

Beans Created by Auto-Configuration:

  • entityManagerFactory (LocalContainerEntityManagerFactoryBean) - Primary JPA EntityManagerFactory
  • transactionManager (JpaTransactionManager) - Transaction manager for JPA operations
  • jpaVendorAdapter (HibernateJpaVendorAdapter) - Hibernate vendor adapter
  • entityManagerFactoryBuilder (EntityManagerFactoryBuilder) - Builder for additional EntityManagerFactory instances
  • persistenceManagedTypes (PersistenceManagedTypes) - Auto-scanned entity types
  • openEntityManagerInViewInterceptor (OpenEntityManagerInViewInterceptor) - Request-scoped EntityManager binding (web apps)

Configuration Properties

Configure JPA and Hibernate behavior through application.properties or application.yml.

JPA Properties (spring.jpa.*)

@ConfigurationProperties(prefix = "spring.jpa")
public class JpaProperties {
    // spring.jpa.properties - Additional JPA/Hibernate properties
    private Map<String, String> properties = new HashMap<>();

    // spring.jpa.mapping-resources - Persistence XML mapping files
    private final List<String> mappingResources = new ArrayList<>();

    // spring.jpa.database-platform - Database dialect class name
    private String databasePlatform;

    // spring.jpa.database - Target database enum (AUTO-DETECTED by default)
    private Database database;

    // spring.jpa.generate-ddl - Initialize schema on startup (default: false)
    private boolean generateDdl = false;

    // spring.jpa.show-sql - Enable SQL logging (default: false)
    private boolean showSql = false;

    // spring.jpa.open-in-view - Enable OpenEntityManagerInView pattern (default: true)
    private Boolean openInView;

    // Getter and setter methods
    public Map<String, String> getProperties() { }
    public void setProperties(Map<String, String> properties) { }

    public List<String> getMappingResources() { }

    public String getDatabasePlatform() { }
    public void setDatabasePlatform(String databasePlatform) { }

    public Database getDatabase() { }
    public void setDatabase(Database database) { }

    public boolean isGenerateDdl() { }
    public void setGenerateDdl(boolean generateDdl) { }

    public boolean isShowSql() { }
    public void setShowSql(boolean showSql) { }

    public Boolean getOpenInView() { }
    public void setOpenInView(Boolean openInView) { }

    // Note: spring.jpa.defer-datasource-initialization is not in JpaProperties class
    // It's a separate property handled by Spring Boot's database initialization system
    // spring.jpa.defer-datasource-initialization - Defer DataSource initialization
    // until after EntityManagerFactory beans are created (default: false)
}

Common Configuration Examples:

# Database connection
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=user
spring.datasource.password=pass

# JPA settings
spring.jpa.show-sql=true
spring.jpa.open-in-view=false
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect

# Defer datasource initialization until after JPA is ready
# Useful when using script-based initialization with JPA schema generation
spring.jpa.defer-datasource-initialization=false

# Additional Hibernate properties
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.jdbc.batch_size=20

Hibernate Properties (spring.jpa.hibernate.*)

@ConfigurationProperties(prefix = "spring.jpa.hibernate")
public class HibernateProperties {
    // spring.jpa.hibernate.ddl-auto - DDL mode (none, validate, update, create, create-drop)
    private String ddlAuto;

    // Nested naming configuration
    private Naming naming = new Naming();

    /**
     * Get the DDL auto mode.
     *
     * @return The DDL auto mode
     */
    public String getDdlAuto() { }

    /**
     * Set the DDL auto mode.
     *
     * @param ddlAuto The DDL auto mode to set
     */
    public void setDdlAuto(String ddlAuto) { }

    /**
     * Get the naming strategy configuration.
     *
     * @return The naming configuration
     */
    public Naming getNaming() { }

    /**
     * Determine Hibernate configuration properties based on JPA properties and settings.
     *
     * @param jpaProperties Standard JPA properties
     * @param settings Hibernate-specific settings
     * @return Map of Hibernate properties to use
     */
    public Map<String, Object> determineHibernateProperties(
        Map<String, String> jpaProperties,
        HibernateSettings settings
    ) { }

    /**
     * Nested class for Hibernate naming strategy configuration.
     */
    public static class Naming {
        // spring.jpa.hibernate.naming.implicit-strategy - Implicit naming strategy class
        private String implicitStrategy;

        // spring.jpa.hibernate.naming.physical-strategy - Physical naming strategy class
        private String physicalStrategy;

        public String getImplicitStrategy() { }
        public void setImplicitStrategy(String implicitStrategy) { }
        public String getPhysicalStrategy() { }
        public void setPhysicalStrategy(String physicalStrategy) { }
    }
}

DDL Auto Values:

  • none - No schema management
  • validate - Validate schema matches entities (production)
  • update - Update schema to match entities (development)
  • create - Drop and recreate schema on startup
  • create-drop - Drop schema on shutdown

Default Naming Strategies:

  • Implicit: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
  • Physical: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy

Configuration Examples:

# Schema management
spring.jpa.hibernate.ddl-auto=update

# Custom naming strategies
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy

Repository Properties (spring.data.jpa.repositories.*)

# Enable/disable repository auto-configuration (default: true)
spring.data.jpa.repositories.enabled=true

# Repository bootstrap mode: default, deferred, lazy (default: default)
spring.data.jpa.repositories.bootstrap-mode=deferred

Bootstrap Modes:

  • default - Repositories initialized eagerly at startup
  • deferred - Repositories initialized when first accessed
  • lazy - Repositories initialized on first actual use

Entity Manager Factory Builder

Fluent builder for creating additional EntityManagerFactory instances with common configuration.

package org.springframework.boot.orm.jpa;

public class EntityManagerFactoryBuilder {
    /**
     * Create a new EntityManagerFactoryBuilder instance.
     *
     * @param jpaVendorAdapter JPA vendor adapter
     * @param jpaPropertiesFactory Factory function for creating JPA properties based on DataSource
     * @param persistenceUnitManager Optional persistence unit manager (can be null)
     * @since 3.4.4
     */
    public EntityManagerFactoryBuilder(
        JpaVendorAdapter jpaVendorAdapter,
        Function<DataSource, Map<String, ?>> jpaPropertiesFactory,
        PersistenceUnitManager persistenceUnitManager
    ) { }

    /**
     * Create a new EntityManagerFactoryBuilder instance with persistence unit root location.
     *
     * @param jpaVendorAdapter JPA vendor adapter
     * @param jpaPropertiesFactory Factory function for creating JPA properties based on DataSource
     * @param persistenceUnitManager Optional persistence unit manager (can be null)
     * @param persistenceUnitRootLocation Persistence unit root location (can be null)
     * @since 3.4.4
     */
    public EntityManagerFactoryBuilder(
        JpaVendorAdapter jpaVendorAdapter,
        Function<DataSource, Map<String, ?>> jpaPropertiesFactory,
        PersistenceUnitManager persistenceUnitManager,
        URL persistenceUnitRootLocation
    ) { }

    /**
     * Create a new EntityManagerFactoryBuilder instance.
     *
     * @param jpaVendorAdapter JPA vendor adapter
     * @param jpaProperties Static JPA properties map
     * @param persistenceUnitManager Optional persistence unit manager (can be null)
     * @deprecated since 3.4.4 for removal in 4.0.0 - Use constructor with Function parameter instead
     */
    @Deprecated(since = "3.4.4", forRemoval = true)
    public EntityManagerFactoryBuilder(
        JpaVendorAdapter jpaVendorAdapter,
        Map<String, ?> jpaProperties,
        PersistenceUnitManager persistenceUnitManager
    ) { }

    /**
     * Create a new EntityManagerFactoryBuilder instance with persistence unit root location.
     *
     * @param jpaVendorAdapter JPA vendor adapter
     * @param jpaProperties Static JPA properties map
     * @param persistenceUnitManager Optional persistence unit manager (can be null)
     * @param persistenceUnitRootLocation Persistence unit root location (can be null)
     * @deprecated since 3.4.4 for removal in 4.0.0 - Use constructor with Function parameter instead
     * @since 1.4.1
     */
    @Deprecated(since = "3.4.4", forRemoval = true)
    public EntityManagerFactoryBuilder(
        JpaVendorAdapter jpaVendorAdapter,
        Map<String, ?> jpaProperties,
        PersistenceUnitManager persistenceUnitManager,
        URL persistenceUnitRootLocation
    ) { }

    /**
     * Start building an EntityManagerFactory with a DataSource
     */
    public Builder dataSource(DataSource dataSource) { }

    /**
     * Set async bootstrap executor for background initialization
     */
    public void setBootstrapExecutor(AsyncTaskExecutor bootstrapExecutor) { }

    /**
     * Set persistence unit post processors
     */
    public void setPersistenceUnitPostProcessors(
        PersistenceUnitPostProcessor... postProcessors
    ) { }

    /**
     * Fluent builder for LocalContainerEntityManagerFactoryBean
     */
    public static class Builder {
        /**
         * Set the managed types (entities) for this EntityManagerFactory
         */
        public Builder managedTypes(PersistenceManagedTypes managedTypes) { }

        /**
         * Set packages to scan for entities
         */
        public Builder packages(String... packagesToScan) { }

        /**
         * Set packages to scan using classes as package markers
         *
         * @param basePackageClasses Classes whose packages should be scanned
         * @return Builder for fluent API
         */
        public Builder packages(Class<?>... basePackageClasses) { }

        /**
         * Set persistence unit name
         */
        public Builder persistenceUnit(String persistenceUnitName) { }

        /**
         * Set JPA properties
         */
        public Builder properties(Map<String, ?> properties) { }

        /**
         * Set mapping resources (XML mappings)
         */
        public Builder mappingResources(String... mappingResources) { }

        /**
         * Enable JTA transaction mode
         */
        public Builder jta(boolean jta) { }

        /**
         * Build the LocalContainerEntityManagerFactoryBean
         */
        public LocalContainerEntityManagerFactoryBean build() { }
    }
}

Usage Example - Multiple DataSources:

@Configuration
public class MultiDataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSourceProperties primaryDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    public DataSource primaryDataSource() {
        return primaryDataSourceProperties()
            .initializeDataSourceBuilder()
            .build();
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("primaryDataSource") DataSource dataSource) {
        return builder
            .dataSource(dataSource)
            .packages("com.example.primary.domain")
            .persistenceUnit("primary")
            .build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSourceProperties secondaryDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    public DataSource secondaryDataSource() {
        return secondaryDataSourceProperties()
            .initializeDataSourceBuilder()
            .build();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("secondaryDataSource") DataSource dataSource) {
        return builder
            .dataSource(dataSource)
            .packages("com.example.secondary.domain")
            .persistenceUnit("secondary")
            .build();
    }
}

Customization Interfaces

Customize Hibernate and EntityManagerFactory configuration through callback interfaces.

HibernatePropertiesCustomizer

package org.springframework.boot.autoconfigure.orm.jpa;

@FunctionalInterface
public interface HibernatePropertiesCustomizer {
    /**
     * Customize Hibernate properties before EntityManagerFactory creation.
     *
     * @param hibernateProperties Map of Hibernate properties to customize
     */
    void customize(Map<String, Object> hibernateProperties);
}

Usage Example:

@Configuration
public class HibernateConfig {

    @Bean
    public HibernatePropertiesCustomizer hibernatePropertiesCustomizer() {
        return (hibernateProperties) -> {
            // Enable Hibernate statistics
            hibernateProperties.put("hibernate.generate_statistics", true);

            // Configure second-level cache
            hibernateProperties.put("hibernate.cache.use_second_level_cache", true);
            hibernateProperties.put("hibernate.cache.region.factory_class",
                "org.hibernate.cache.jcache.JCacheRegionFactory");

            // Configure batch processing
            hibernateProperties.put("hibernate.jdbc.batch_size", 25);
            hibernateProperties.put("hibernate.order_inserts", true);
            hibernateProperties.put("hibernate.order_updates", true);
        };
    }
}

EntityManagerFactoryBuilderCustomizer

package org.springframework.boot.autoconfigure.orm.jpa;

@FunctionalInterface
public interface EntityManagerFactoryBuilderCustomizer {
    /**
     * Customize the EntityManagerFactoryBuilder before it's used.
     *
     * @param builder The EntityManagerFactoryBuilder to customize
     */
    void customize(EntityManagerFactoryBuilder builder);
}

Usage Example:

@Configuration
public class JpaConfig {

    @Bean
    public EntityManagerFactoryBuilderCustomizer entityManagerFactoryBuilderCustomizer() {
        return (builder) -> {
            // Configure async bootstrap for faster startup
            builder.setBootstrapExecutor(asyncTaskExecutor());

            // Add custom persistence unit post processors
            builder.setPersistenceUnitPostProcessors(
                new CustomPersistenceUnitPostProcessor()
            );
        };
    }

    @Bean
    public AsyncTaskExecutor asyncTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setThreadNamePrefix("jpa-bootstrap-");
        executor.initialize();
        return executor;
    }
}

Initialization Order Control

Control EntityManagerFactory initialization order relative to other beans.

package org.springframework.boot.autoconfigure.orm.jpa;

public class EntityManagerFactoryDependsOnPostProcessor
        extends AbstractDependsOnBeanFactoryPostProcessor {

    /**
     * Make EntityManagerFactory depend on specific bean names.
     *
     * @param dependsOn Bean names that EntityManagerFactory should depend on
     */
    public EntityManagerFactoryDependsOnPostProcessor(String... dependsOn) { }

    /**
     * Make EntityManagerFactory depend on beans of specific types.
     *
     * @param dependsOn Bean types that EntityManagerFactory should depend on
     */
    public EntityManagerFactoryDependsOnPostProcessor(Class<?>... dependsOn) { }
}

Usage Example:

@Configuration
public class InitializationOrderConfig {

    /**
     * Ensure EntityManagerFactory initializes after Flyway migrations
     */
    @Bean
    public static EntityManagerFactoryDependsOnPostProcessor
            entityManagerFactoryDependsOnPostProcessor() {
        return new EntityManagerFactoryDependsOnPostProcessor(Flyway.class);
    }
}

Naming Strategies

Customize how entity and column names are mapped to database table and column names.

package org.springframework.boot.orm.jpa.hibernate;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitJoinTableNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;

/**
 * Spring's default implicit naming strategy.
 * Join tables use format: {owning_table}_{property_name}
 * Identical to ImplicitNamingStrategyJpaCompliantImpl except for join table naming.
 */
public class SpringImplicitNamingStrategy
        extends ImplicitNamingStrategyJpaCompliantImpl {

    /**
     * Determine the implicit name for a join table.
     * Uses format: {owning_physical_table_name}_{association_owning_property_name}
     *
     * @param source The source metadata for the join table
     * @return The identifier for the join table name
     */
    @Override
    public Identifier determineJoinTableName(ImplicitJoinTableNameSource source) { }
}

Providing Custom Naming Strategy:

@Configuration
public class NamingStrategyConfig {

    /**
     * Override the default physical naming strategy
     */
    @Bean
    public PhysicalNamingStrategy physicalNamingStrategy() {
        // Use standard JPA naming (no snake_case conversion)
        return new PhysicalNamingStrategyStandardImpl();
    }

    /**
     * Override the default implicit naming strategy
     */
    @Bean
    public ImplicitNamingStrategy implicitNamingStrategy() {
        return new SpringImplicitNamingStrategy();
    }
}

Or via properties:

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy

Entity Scanning

Control which packages are scanned for JPA entities.

Default Behavior: Auto-scans main application package and sub-packages.

Custom Scanning:

@Configuration
@EntityScan(basePackages = {
    "com.example.domain",
    "com.example.shared.entities"
})
public class EntityScanConfig {
}

Entity Configuration:

@Entity
@Table(name = "products", indexes = {
    @Index(name = "idx_product_sku", columnList = "sku"),
    @Index(name = "idx_product_category", columnList = "category_id")
})
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false, length = 50)
    private String sku;

    @Column(nullable = false)
    private String name;

    @Column(precision = 10, scale = 2)
    private BigDecimal price;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id")
    private Category category;

    @OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Review> reviews = new ArrayList<>();

    // Getters and setters
}

Spring Data JPA Repositories

Create repository interfaces that Spring Data JPA automatically implements.

Repository Interfaces:

// CrudRepository - Basic CRUD operations
public interface CrudRepository<T, ID> extends Repository<T, ID> {
    <S extends T> S save(S entity);
    <S extends T> Iterable<S> saveAll(Iterable<S> entities);
    Optional<T> findById(ID id);
    boolean existsById(ID id);
    Iterable<T> findAll();
    Iterable<T> findAllById(Iterable<ID> ids);
    long count();
    void deleteById(ID id);
    void delete(T entity);
    void deleteAllById(Iterable<? extends ID> ids);
    void deleteAll(Iterable<? extends T> entities);
    void deleteAll();
}

// JpaRepository - Adds JPA-specific methods
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID> {
    List<T> findAll();
    List<T> findAll(Sort sort);
    List<T> findAllById(Iterable<ID> ids);
    <S extends T> List<S> saveAll(Iterable<S> entities);
    void flush();
    <S extends T> S saveAndFlush(S entity);
    <S extends T> List<S> saveAllAndFlush(Iterable<S> entities);
    void deleteInBatch(Iterable<T> entities);
    void deleteAllInBatch(Iterable<T> entities);
    void deleteAllByIdInBatch(Iterable<ID> ids);
    void deleteAllInBatch();
    T getOne(ID id);  // Deprecated, use getReferenceById
    T getReferenceById(ID id);
}

// PagingAndSortingRepository - Pagination and sorting support
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort sort);
    Page<T> findAll(Pageable pageable);
}

Query Methods: Spring Data JPA derives queries from method names.

public interface UserRepository extends JpaRepository<User, Long> {
    // Derived query methods
    List<User> findByName(String name);
    List<User> findByNameAndEmail(String name, String email);
    List<User> findByNameOrEmail(String name, String email);
    List<User> findByAgeBetween(int minAge, int maxAge);
    List<User> findByNameContaining(String namePart);
    List<User> findByNameStartingWith(String prefix);
    List<User> findByNameIgnoreCase(String name);
    List<User> findByOrderByNameAsc();
    Optional<User> findFirstByEmail(String email);

    // With pagination and sorting
    Page<User> findByName(String name, Pageable pageable);
    List<User> findByAge(int age, Sort sort);

    // Count and exists queries
    long countByAge(int age);
    boolean existsByEmail(String email);

    // Delete queries
    long deleteByAge(int age);
    void removeByName(String name);

    // Custom JPQL queries
    @Query("SELECT u FROM User u WHERE u.age > :age")
    List<User> findUsersOlderThan(@Param("age") int age);

    @Query("SELECT u FROM User u WHERE u.name LIKE %:namePart%")
    List<User> searchByName(@Param("namePart") String namePart);

    // Native SQL queries
    @Query(value = "SELECT * FROM users WHERE email = ?1", nativeQuery = true)
    User findByEmailNative(String email);

    // Modifying queries
    @Modifying
    @Query("UPDATE User u SET u.active = :active WHERE u.id = :id")
    int updateActiveStatus(@Param("id") Long id, @Param("active") boolean active);
}

Pagination Example:

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public Page<User> getUsers(int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending());
        return userRepository.findAll(pageable);
    }

    public Page<User> searchUsers(String name, int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return userRepository.findByName(name, pageable);
    }
}

Transaction Management

JPA transactions are managed automatically through Spring's @Transactional annotation.

Transaction Configuration:

@Service
public class OrderService {
    private final OrderRepository orderRepository;
    private final InventoryService inventoryService;

    public OrderService(OrderRepository orderRepository,
                       InventoryService inventoryService) {
        this.orderRepository = orderRepository;
        this.inventoryService = inventoryService;
    }

    /**
     * Transactional method - changes committed or rolled back together
     */
    @Transactional
    public Order createOrder(OrderRequest request) {
        // Check inventory
        inventoryService.reserveItems(request.getItems());

        // Create order
        Order order = new Order();
        order.setCustomerId(request.getCustomerId());
        order.setItems(request.getItems());
        order.setStatus(OrderStatus.PENDING);

        return orderRepository.save(order);
        // Transaction commits here if no exception thrown
    }

    /**
     * Read-only transaction (optimization)
     */
    @Transactional(readOnly = true)
    public Order getOrder(Long orderId) {
        return orderRepository.findById(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
    }

    /**
     * Custom transaction configuration
     */
    @Transactional(
        propagation = Propagation.REQUIRES_NEW,
        isolation = Isolation.SERIALIZABLE,
        timeout = 30,
        rollbackFor = BusinessException.class
    )
    public void processRefund(Long orderId) {
        // Process refund in new transaction
    }
}

JTA Integration

JTA platform automatically configured when JTA transaction manager is detected.

package org.springframework.boot.orm.jpa.hibernate;

import jakarta.transaction.TransactionManager;
import jakarta.transaction.UserTransaction;
import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;
import org.springframework.transaction.jta.JtaTransactionManager;

/**
 * Hibernate JTA platform that integrates with Spring's JtaTransactionManager.
 * Automatically configured when JTA is detected.
 */
public class SpringJtaPlatform extends AbstractJtaPlatform {
    /**
     * Create JTA platform with Spring's transaction manager.
     *
     * @param transactionManager The JTA transaction manager
     */
    public SpringJtaPlatform(JtaTransactionManager transactionManager) { }

    /**
     * Locate the TransactionManager from the Spring JtaTransactionManager.
     *
     * @return The JTA TransactionManager
     */
    @Override
    protected TransactionManager locateTransactionManager() { }

    /**
     * Locate the UserTransaction from the Spring JtaTransactionManager.
     *
     * @return The JTA UserTransaction
     */
    @Override
    protected UserTransaction locateUserTransaction() { }
}

Types

// Database enum for spring.jpa.database property
// Package: org.springframework.orm.jpa.vendor (Spring Framework)
public enum Database {
    DEFAULT,
    DB2,
    DERBY,
    H2,
    HANA,
    HSQL,
    INFORMIX,
    MYSQL,
    ORACLE,
    POSTGRESQL,
    SQL_SERVER,
    SYBASE
}

// Bootstrap mode for repository initialization
// Package: org.springframework.data.repository.config (Spring Data Commons)
public enum BootstrapMode {
    DEFAULT,    // Eager initialization at startup
    DEFERRED,   // Initialize when first accessed
    LAZY        // Initialize on first actual use
}

// Hibernate settings container (internal use)
public class HibernateSettings {
    /**
     * Set the DDL auto supplier and return this instance for method chaining.
     *
     * @param ddlAuto Supplier for DDL auto mode
     * @return This HibernateSettings instance
     */
    public HibernateSettings ddlAuto(Supplier<String> ddlAuto) { }

    /**
     * Get the DDL auto mode.
     *
     * @return DDL auto mode or null if not set
     */
    public String getDdlAuto() { }

    /**
     * Set the Hibernate properties customizers and return this instance for method chaining.
     *
     * @param customizers Collection of customizers to apply
     * @return This HibernateSettings instance
     */
    public HibernateSettings hibernatePropertiesCustomizers(
        Collection<HibernatePropertiesCustomizer> customizers
    ) { }

    /**
     * Get the Hibernate properties customizers.
     *
     * @return Collection of customizers
     */
    public Collection<HibernatePropertiesCustomizer> getHibernatePropertiesCustomizers() { }
}

Dependencies

This starter aggregates the following dependencies:

  • spring-boot-starter - Core Spring Boot functionality
  • spring-boot-starter-jdbc - JDBC and DataSource support
  • hibernate-core (org.hibernate.orm) - Hibernate ORM implementation
  • spring-data-jpa (org.springframework.data) - Spring Data JPA repositories
  • spring-aspects (org.springframework) - AOP support for @Transactional

Common Patterns

Testing with @DataJpaTest

@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private TestEntityManager entityManager;

    @Test
    void testFindByName() {
        // Given
        User user = new User();
        user.setName("Alice");
        user.setEmail("alice@example.com");
        entityManager.persist(user);
        entityManager.flush();

        // When
        List<User> users = userRepository.findByName("Alice");

        // Then
        assertThat(users).hasSize(1);
        assertThat(users.get(0).getEmail()).isEqualTo("alice@example.com");
    }
}

Auditing

Enable automatic auditing of created/modified dates and users:

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {

    @Bean
    public AuditorAware<String> auditorProvider() {
        // Return current user from security context
        return () -> Optional.of(
            SecurityContextHolder.getContext()
                .getAuthentication()
                .getName()
        );
    }
}

@Entity
@EntityListeners(AuditingEntityListener.class)
public class AuditableEntity {

    @CreatedDate
    @Column(nullable = false, updatable = false)
    private Instant createdDate;

    @LastModifiedDate
    private Instant lastModifiedDate;

    @CreatedBy
    @Column(nullable = false, updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;
}

Specifications (Dynamic Queries)

public interface UserRepository extends JpaRepository<User, Long>,
                                        JpaSpecificationExecutor<User> {
}

public class UserSpecifications {

    public static Specification<User> hasName(String name) {
        return (root, query, cb) ->
            name == null ? null : cb.equal(root.get("name"), name);
    }

    public static Specification<User> isActive() {
        return (root, query, cb) -> cb.isTrue(root.get("active"));
    }

    public static Specification<User> ageGreaterThan(int age) {
        return (root, query, cb) -> cb.greaterThan(root.get("age"), age);
    }
}

// Usage
@Service
public class UserSearchService {
    private final UserRepository userRepository;

    public List<User> searchUsers(String name, Integer minAge, Boolean active) {
        Specification<User> spec = Specification.where(null);

        if (name != null) {
            spec = spec.and(UserSpecifications.hasName(name));
        }
        if (minAge != null) {
            spec = spec.and(UserSpecifications.ageGreaterThan(minAge));
        }
        if (Boolean.TRUE.equals(active)) {
            spec = spec.and(UserSpecifications.isActive());
        }

        return userRepository.findAll(spec);
    }
}