JPA 自动配置
概述
Spring Boot 通过 JpaRepositoriesAutoConfiguration 和 HibernateJpaAutoConfiguration 自动配置 JPA 与 Hibernate 集成,核心包括 EntityManagerFactory 创建、PlatformTransactionManager 绑定、OpenEntityManagerInViewInterceptor 注册、审计支持等。
本文将拆解 JPA 自动配置的 8 个关键细节,涵盖条件触发、属性映射、连接池元数据暴露等核心内容。
本文基于 Spring Boot 3.2.5 + Hibernate 6.x 源码分析。
1. JpaRepositoriesAutoConfiguration 的触发
JpaRepositoriesAutoConfiguration 是 JPA Repository 机制自动配置的入口,其触发条件如下:
@AutoConfiguration(after = {HibernateJpaAutoConfiguration.class, TaskExecutionAutoConfiguration.class})
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(EntityManager.class)
@EnableJpaRepositories
public class JpaRepositoriesAutoConfiguration {
// ...
}| 条件 | 说明 |
|---|---|
@AutoConfiguration(after = {HibernateJpaAutoConfiguration.class, ...}) | 在 Hibernate JPA 自动配置和任务执行配置之后执行 |
@ConditionalOnBean(DataSource.class) | 容器中必须存在 DataSource Bean |
@ConditionalOnClass(EntityManager.class) | classpath 中必须存在 EntityManager(jakarta.persistence 依赖) |
@EnableJpaRepositories | 启用 JPA Repository 扫描,注册 JpaRepositoryFactoryBean |
当上述条件满足时,Spring Boot 会扫描所有继承 JpaRepository 接口的 Repository 类,并为其生成代理实现。
核心流程:
JpaRepositoriesAutoConfiguration 触发
↓
@EnableJpaRepositories → 导入 JpaRepositoriesRegistrar
↓
JpaRepositoryFactoryBean.getObject() → 创建 Repository 代理
↓
SimpleJpaRepository 实现 CRUD2. EntityManagerFactoryBuilder 创建 LocalContainerEntityManagerFactoryBean
EntityManagerFactoryBuilder 负责构建 LocalContainerEntityManagerFactoryBean,这是 JPA EntityManagerFactory 的核心实现。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
@EnableConfigurationProperties(JpaProperties.class)
public class HibernateJpaConfiguration {
@Bean
@ConditionalOnMissingBean(EntityManagerFactoryBuilder.class)
public EntityManagerFactoryBuilder entityManagerFactoryBuilder(
JpaProperties jpaProperties,
ObjectProvider<DataSource> dataSource,
ObjectProvider<PersistenceUnitManager> persistenceUnitManager) {
return new EntityManagerFactoryBuilder(
new JpaVendorAdapter() { /* HibernateJpaVendorAdapter */ },
jpaProperties.getProperties(), // spring.jpa.properties.*
persistenceUnitManager.getIfAvailable());
}
}LocalContainerEntityManagerFactoryBean 创建流程:
public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManagerFactoryBean {
@Override
protected EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException {
// 1. 创建 PersistenceProvider(Hibernate 实现)
PersistenceProvider provider = getPersistenceProvider();
// 2. 创建 PersistenceUnitInfo
PersistenceUnitInfo info = determinePersistenceUnitInfo();
// 3. 调用 provider.createContainerEntityManagerFactory()
return provider.createContainerEntityManagerFactory(info, getJpaPropertyMap());
}
}PersistenceProvider.createContainerEntityManagerFactory() 调用链:
LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory()
↓
PersistenceProvider.createContainerEntityManagerFactory(puInfo, properties)
↓
HibernatePersistenceProvider.createContainerEntityManagerFactory()
↓
EntityManagerFactoryBuilderImpl.build()
↓
SessionFactoryImpl(SessionFactory)最终返回的 SessionFactoryImpl 是 Hibernate 的 EntityManagerFactory 实现,内部包含:
SessionFactoryImpl.sessionFactoryOptions— 配置选项SessionFactoryImpl.metamodel— 实体元模型SessionFactoryImpl.queryCache/sessionFactoryImpl.cache— 二级缓存SessionFactoryImpl.serviceRegistry— 服务注册(JDBC、事务等)
3. HibernateJpaAutoConfiguration 在 DataSourceAutoConfiguration 之后
HibernateJpaAutoConfiguration 负责自动配置 Hibernate 特定的 JPA 实现,其配置次序非常重要:
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
@ConditionalOnClass({LocalContainerEntityManagerFactoryBean.class, EntityManager.class, SessionFactory.class})
@EnableConfigurationProperties(HibernateProperties.class)
public class HibernateJpaAutoConfiguration {
// ...
}配置次序链:
DataSourceAutoConfiguration(先)→ DataSource 就绪
↓
HibernateJpaAutoConfiguration(后)→ 创建 EntityManagerFactory
↓
JpaRepositoriesAutoConfiguration(最后)→ 创建 Repository 代理HibernateJpaAutoConfiguration 创建的核心 Bean:
| Bean | 说明 |
|---|---|
LocalContainerEntityManagerFactoryBean | EntityManagerFactory 实例 |
JpaTransactionManager | JPA 事务管理器,包装 EntityManagerFactory |
EntityManagerFactoryBuilder | 构建器,用于创建 EntityManagerFactory |
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager(
EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}4. HibernateProperties 的 hibernate.* 属性映射
HibernateProperties 负责将 Spring Boot 的 spring.jpa.hibernate.* 配置项映射为 Hibernate 原生属性。
@ConfigurationProperties(prefix = "spring.jpa.hibernate")
public class HibernateProperties {
private String ddlAuto; // spring.jpa.hibernate.ddl-auto
private NamingStrategy naming; // spring.jpa.hibernate.naming.*
private boolean useNewIdGeneratorMappings = true; // ID 生成器
// ...
public Map<String, Object> determineHibernateProperties(
Map<String, Object> additionalProperties) {
Map<String, Object> result = new LinkedHashMap<>(additionalProperties);
// ddl-auto 映射
if (this.ddlAuto != null) {
result.put("hibernate.hbm2ddl.auto", this.ddlAuto);
}
// 命名策略映射
if (this.naming.getPhysicalStrategy() != null) {
result.put("hibernate.physical_naming_strategy",
this.naming.getPhysicalStrategy());
}
if (this.naming.getImplicitStrategy() != null) {
result.put("hibernate.implicit_naming_strategy",
this.naming.getImplicitStrategy());
}
return result;
}
}常见属性映射表:
| Spring Boot 配置 | Hibernate 原生属性 | 说明 |
|---|---|---|
spring.jpa.hibernate.ddl-auto | hibernate.hbm2ddl.auto | DDL 策略(create/update/validate/none) |
spring.jpa.hibernate.naming.physical-strategy | hibernate.physical_naming_strategy | 物理命名策略(表名/列名转换) |
spring.jpa.hibernate.naming.implicit-strategy | hibernate.implicit_naming_strategy | 隐式命名策略 |
spring.jpa.hibernate.use-new-id-generator-mappings | hibernate.id.new_generator_mappings | 新版 ID 生成器 |
spring.jpa.show-sql | hibernate.show_sql | 打印 SQL(注意:Spring Boot 属性) |
spring.jpa.properties.hibernate.* | 直接传递 | 透传所有 spring.jpa.properties.* |
application.yml 配置示例:
spring:
jpa:
hibernate:
ddl-auto: validate
naming:
physical-strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
properties:
hibernate:
format_sql: true
default_batch_fetch_size: 1005. spring.jpa.open-in-view=true 默认开启
Spring Boot 默认开启 spring.jpa.open-in-view=true,这会在请求期间保持 EntityManager 打开,避免懒加载 LazyInitializationException。
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(OpenEntityManagerInViewInterceptor.class)
@ConditionalOnProperty(prefix = "spring.jpa", name = "open-in-view", havingValue = "true", matchIfMissing = true)
public class JpaBaseConfiguration {
@Bean
public OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor() {
return new OpenEntityManagerInViewInterceptor();
}
}OpenEntityManagerInViewInterceptor 的工作原理:
public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAccessor {
// preHandle:在 Controller 方法执行前绑定 EntityManager 到当前线程
@Override
public void preHandle(HttpServletRequest request, HttpServletResponse response) {
EntityManager em = createEntityManager();
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), em);
}
// afterCompletion:在视图渲染完成后关闭 EntityManager
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) {
EntityManager em = TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
em.close();
}
}请求生命周期:
请求进入
↓
DispatcherServlet.doDispatch()
↓
HandlerExecutionChain.applyPreHandle()
↓ OpenEntityManagerInViewInterceptor.preHandle()
↓ 创建 EntityManager -> 绑定到线程
Controller 方法执行(可进行懒加载)
↓
HandlerExecutionChain.applyPostHandle()
↓
视图渲染(可进行懒加载)
↓
HandlerExecutionChain.triggerAfterCompletion()
↓ OpenEntityManagerInViewInterceptor.afterCompletion()
↓ 关闭 EntityManager
响应返回⚠️ 注意:
open-in-view虽然方便,但在高并发场景下可能导致连接池耗尽,因为EntityManager会在整个请求期间保持数据库连接。建议在性能敏感场景中关闭(spring.jpa.open-in-view=false),并显式使用@Transactional或JOIN FETCH管理懒加载。
6. PersistenceAnnotationBeanPostProcessor 处理 @PersistenceContext
PersistenceAnnotationBeanPostProcessor 是 Spring 内部的后置处理器,负责处理 @PersistenceContext 和 @PersistenceUnit 注解的注入。
public class PersistenceAnnotationBeanPostProcessor implements BeanPostProcessor {
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 查找 Bean 中所有 @PersistenceContext 和 @PersistenceUnit 注解
InjectionMetadata metadata = findPersistenceMetadata(beanName, bean.getClass());
metadata.inject(bean, beanName, pvs);
return pvs;
}
}注入方式:
@Component
public class UserRepository {
// 方式 1:注入 EntityManager(线程安全,由 EntityManagerProxy 代理)
@PersistenceContext
private EntityManager entityManager;
// 方式 2:注入 EntityManagerFactory
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
public User findById(Long id) {
// 线程安全使用
return entityManager.createQuery("SELECT u FROM User u WHERE u.id = :id", User.class)
.setParameter("id", id)
.getSingleResult();
}
}@PersistenceContext 的实现细节:
// PersistenceAnnotationBeanPostProcessor 内部处理逻辑
private InjectionMetadata buildPersistenceMetadata(Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
for (Field field : clazz.getDeclaredFields()) {
PersistenceContext pc = field.getAnnotation(PersistenceContext.class);
if (pc != null) {
elements.add(new PersistenceElement(field, pc, null));
}
}
return new InjectionMetadata(clazz, elements);
}
// PersistenceElement.inject() 实现
// 实际上注入的是 SharedEntityManagerInvocationHandler 代理
// 每次调用会从 EntityManagerFactory 获取一个新的 EntityManager关键点:
@PersistenceContext注入的实际是SharedEntityManagerInvocationHandler代理对象- 每次使用方法调用时,代理会通过
EntityManagerFactory.createEntityManager()创建新的EntityManager - 这保证了线程安全,避免了同一个
EntityManager在多个线程间共享 - 如果存在
@Transactional注解,则共享事务范围内的同一个EntityManager
7. DataSourcePoolMetadataProviders 暴露连接池元数据
DataSourcePoolMetadataProviders 负责暴露连接池的运行指标,供 Actuator health/db 端点使用。
@Configuration(proxyBeanMethods = false)
public class DataSourcePoolMetadataProvidersConfiguration {
// 支持 HikariCP
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(com.zaxxer.hikari.HikariDataSource.class)
static class HikariPoolDataSourceMetadataProviderConfiguration {
@Bean
DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() {
return (DataSource dataSource) -> {
if (dataSource instanceof HikariDataSource hikari) {
return new HikariDataSourcePoolMetadata(hikari);
}
return null;
};
}
}
// 支持 Tomcat JDBC Pool
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
static class TomcatDataSourcePoolMetadataProviderConfiguration {
@Bean
DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() {
return (DataSource dataSource) -> {
if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource tomcat) {
return new TomcatDataSourcePoolMetadata(tomcat);
}
return null;
};
}
}
// 支持 DBCP2
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.commons.dbcp2.BasicDataSource.class)
static class Dbcp2PoolDataSourceMetadataProviderConfiguration {
// ...
}
// 支持 Oracle UCP
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(oracle.ucp.jdbc.PoolDataSource.class)
static class OracleUcpPoolDataSourceMetadataProviderConfiguration {
// ...
}
}DataSourcePoolMetadata 接口:
public interface DataSourcePoolMetadata {
// 当前活跃连接数
Integer getActive();
// 最大连接数
Integer getMax();
// 最小空闲连接数
Integer getMin();
// 当前正在使用的连接数
default Integer getUsage() {
Integer max = getMax();
return max != null ? (getActive() * 100) / max : null;
}
}HikariCP 实现:
public class HikariDataSourcePoolMetadata extends DataSourcePoolMetadata<HikariDataSource> {
@Override
public Integer getActive() {
return getDataSource().getHikariPoolMXBean().getActiveConnections();
}
@Override
public Integer getMax() {
return getDataSource().getHikariPoolMXBean().getTotalConnections();
}
@Override
public Integer getMin() {
return getDataSource().getHikariPoolMXBean().getIdleConnections();
}
}Actuator 使用:
// DataSourceHealthIndicator.getHealth()
// 调用 DataSourcePoolMetadataProvider 获取连接池指标
@Override
protected void doHealthCheck(Health.Builder builder) {
DataSourcePoolMetadata poolMetadata = this.dataSourcePoolMetadataProvider
.getDataSourcePoolMetadata(this.dataSource);
builder.withDetail("database", this.product);
builder.withDetail("validationQuery", this.validationQuery);
if (poolMetadata != null) {
builder.withDetail("active", poolMetadata.getActive());
builder.withDetail("max", poolMetadata.getMax());
builder.withDetail("usage", poolMetadata.getUsage()); // 使用率百分比
}
}Actuator 健康检查响应示例:
{
"status": "UP",
"components": {
"db": {
"status": "UP",
"details": {
"database": "MySQL",
"validationQuery": "SELECT 1",
"active": 3,
"max": 20,
"usage": 15.0
}
}
}
}8. @EnableJpaAuditing 自动配置
@EnableJpaAuditing 是 Spring Data JPA 提供的注解,用于自动填充实体中的审计字段(如创建时间、修改时间等)。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(JpaAuditingRegistrar.class)
public @interface EnableJpaAuditing {
String auditorAwareRef() default ""; // 获取当前用户的 Bean 名称
boolean setDates() default true; // 是否自动设置日期
DateTimeProvider dateTimeProviderRef() default ""; // 时间提供者
boolean modifyOnCreate() default true; // 创建时是否设置修改时间
}JpaAuditingRegistrar 注册的 Bean:
class JpaAuditingRegistrar extends ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotation, BeanDefinitionRegistry registry) {
// 注册 JpaAuditingHandler
GenericBeanDefinition handlerDef = new GenericBeanDefinition();
handlerDef.setBeanClass(JpaAuditingHandler.class);
// ...
// 注册 AuditingEntityListener
GenericBeanDefinition listenerDef = new GenericBeanDefinition();
listenerDef.setBeanClass(AuditingEntityListener.class);
// ...
}
}实体使用:
@Entity
@EntityListeners(AuditingEntityListener.class) // 注册审计监听器
public class Order {
@Id
@GeneratedValue
private Long id;
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@CreatedBy
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
}AuditingEntityListener 实现:
public class AuditingEntityListener {
private JpaAuditingHandler handler;
@PrePersist
public void touchForCreate(Object target) {
handler.touch(target); // 设置创建时间、修改时间、创建人
}
@PreUpdate
public void touchForUpdate(Object target) {
handler.touch(target); // 设置修改时间、修改人
}
}JpaAuditingHandler 内部逻辑:
public class JpaAuditingHandler implements AuditableBeanWrapperFactory {
public <T> void touch(T target) {
AuditableBeanWrapper wrapper = getAuditableBeanWrapper(target);
if (wrapper == null) return;
// 设置创建时间(仅当为 null 时)
if (wrapper.getCreatedDate() == null) {
wrapper.setCreatedDate(dateTimeProvider.getNow().orElse(LocalDateTime.now()));
}
// 设置修改时间
wrapper.setLastModifiedDate(dateTimeProvider.getNow().orElse(LocalDateTime.now()));
// 设置创建人(仅当为 null 时)
if (wrapper.getCreatedBy() == null) {
wrapper.setCreatedBy(auditorAware.getCurrentAuditor().orElse(null));
}
// 设置修改人
wrapper.setLastModifiedBy(auditorAware.getCurrentAuditor().orElse(null));
}
}配置 AuditorAware:
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class JpaAuditingConfig {
@Bean
public AuditorAware<String> auditorAware() {
return () -> Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
.map(Authentication::getName);
}
}总结
JPA 自动配置的 8 个细节点总结如下:
| # | 细节点 | 核心类/机制 |
|---|---|---|
| ① | JpaRepositoriesAutoConfiguration 的触发 | @ConditionalOnBean(DataSource.class) + @ConditionalOnClass(EntityManager.class) |
| ② | EntityManagerFactoryBuilder 创建 LocalContainerEntityManagerFactoryBean | PersistenceProvider.createContainerEntityManagerFactory() |
| ③ | HibernateJpaAutoConfiguration 在 DataSourceAutoConfiguration 之后 | @AutoConfigurationAfter(DataSourceAutoConfiguration.class) |
| ④ | HibernateProperties 的 hibernate.* 属性映射 | ddl-auto → hibernate.hbm2ddl.auto、naming.physical-strategy → hibernate.physical_naming_strategy |
| ⑤ | spring.jpa.open-in-view=true 默认开启 | OpenEntityManagerInViewInterceptor 注册到 HandlerExecutionChain |
| ⑥ | PersistenceAnnotationBeanPostProcessor 处理 @PersistenceContext | @PersistenceContext EntityManager em 注入 |
| ⑦ | DataSourcePoolMetadataProviders 暴露连接池元数据 | DataSourcePoolMetadata.getActive() / getMax() 用于 Actuator health/db 端点 |
| ⑧ | @EnableJpaAuditing 自动配置 | JpaAuditingHandler → AuditingEntityListener |