Starter for using Spring Data JPA with Hibernate
npx @tessl/cli install tessl/maven-org-springframework-boot--spring-boot-starter-data-jpa@3.5.0Spring 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.
pom.xml or build.gradleMaven:
<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'import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.boot.autoconfigure.orm.jpa.*;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import jakarta.persistence.*;// 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=trueThe starter provides several layers of auto-configuration:
HibernateJpaAutoConfiguration and JpaRepositoriesAutoConfiguration automatically detect dependencies and configure beansJpaBaseConfiguration provides EntityManagerFactory, TransactionManager, and related infrastructure beansThe 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 EntityManagerFactorytransactionManager (JpaTransactionManager) - Transaction manager for JPA operationsjpaVendorAdapter (HibernateJpaVendorAdapter) - Hibernate vendor adapterentityManagerFactoryBuilder (EntityManagerFactoryBuilder) - Builder for additional EntityManagerFactory instancespersistenceManagedTypes (PersistenceManagedTypes) - Auto-scanned entity typesopenEntityManagerInViewInterceptor (OpenEntityManagerInViewInterceptor) - Request-scoped EntityManager binding (web apps)Configure JPA and Hibernate behavior through application.properties or application.yml.
@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@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 managementvalidate - Validate schema matches entities (production)update - Update schema to match entities (development)create - Drop and recreate schema on startupcreate-drop - Drop schema on shutdownDefault Naming Strategies:
org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategyorg.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategyConfiguration 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# 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=deferredBootstrap Modes:
default - Repositories initialized eagerly at startupdeferred - Repositories initialized when first accessedlazy - Repositories initialized on first actual useFluent 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();
}
}Customize Hibernate and EntityManagerFactory configuration through callback interfaces.
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);
};
}
}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;
}
}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);
}
}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.SpringImplicitNamingStrategyControl 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
}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);
}
}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 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() { }
}// 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() { }
}This starter aggregates the following dependencies:
spring-boot-starter - Core Spring Boot functionalityspring-boot-starter-jdbc - JDBC and DataSource supporthibernate-core (org.hibernate.orm) - Hibernate ORM implementationspring-data-jpa (org.springframework.data) - Spring Data JPA repositoriesspring-aspects (org.springframework) - AOP support for @Transactional@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");
}
}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;
}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);
}
}