Spring Boot 配置 : JpaBaseConfiguration

本文围绕Spring Boot自动配置展开,介绍了提供配置的抽象基类,针对不同实现有具体实现类。不同提供商实现的子类主要包装信息,配置任务主要落在特定组件上,还说明了其确保的定义内容,最后给出源代码版本和参考文章。

概述

JpaBaseConfigurationSpring Boot提供的对JPA进行配置的抽象基类。针对不同的JPA实现,会有不同的具体实现类。比如Spring Boot内置支持基于HibernateJPA,所以它提供了相应的实现HibernateJpaConfiguration。而如果开发人员想使用其他JPA实现,比如EclipseLink或者OpenJPA,就要继承JpaBaseConfiguration提供相应的具体实现。

虽然针对不同的JPA提供商实现需要有不同的JpaBaseConfiguration实现子类,但这些子类主要是将JPA提供商实现信息包装成统一的格式供JpaBaseConfiguration使用,而Spring Data JPA主要工作组件的配置任务,还是主要落在JpaBaseConfiguration身上。

JpaBaseConfiguration主要是确保以下bean的定义 :

  • PlatformTransactionManager transactionManager

    仅在该类型的bean未被定义的情况下才定义

  • LocalContainerEntityManagerFactoryBean entityManagerFactory

    仅在该类型的bean或者bean EntityManagerFactory未被定义的情况下才定义

  • JpaVendorAdapter jpaVendorAdapter

    仅在该类型的bean未被定义的情况下才定义;
    bean LocalContainerEntityManagerFactoryBean entityManagerFactory定义的基础。

  • EntityManagerFactoryBuilder entityManagerFactoryBuilder

    仅在该类型的bean未被定义的情况下才定义;
    bean LocalContainerEntityManagerFactoryBean entityManagerFactory定义的基础。

可以这么理解,一个JpaBaseConfiguration(当然具体类是它的某个实现子类)应用之后,我们就可以注入bean EntityManagerFactory了。

源代码

源代码版本 : spring-boot-autoconfigure-2.1.3.RELEASE

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

// 省略 imports


@Configuration
// 确保前缀为 spring.jpa 的属性被加载到 bean JpaProperties
@EnableConfigurationProperties(JpaProperties.class)
// 导入 DataSourceInitializedPublisher.Registrar, 用于向容器注册 bean DataSourceInitializedPublisher,
// DataSourceInitializedPublisher 是一个 BeanPostProcessor
@Import(DataSourceInitializedPublisher.Registrar.class)
public abstract class JpaBaseConfiguration implements BeanFactoryAware {

	private final DataSource dataSource;

	private final JpaProperties properties;

	private final JtaTransactionManager jtaTransactionManager;

	private final TransactionManagerCustomizers transactionManagerCustomizers;

	private ConfigurableListableBeanFactory beanFactory;

    // dataSource 在其他地方定义 : 
    // jtaTransactionManager 在其他地方定义 : 
    // transactionManagerCustomizers 在其他地方定义 : 
    // JpaProperties properties 来源参考本类注解
	protected JpaBaseConfiguration(DataSource dataSource, JpaProperties properties,
			ObjectProvider<JtaTransactionManager> jtaTransactionManager,
			ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
		this.dataSource = dataSource;
		this.properties = properties;
		this.jtaTransactionManager = jtaTransactionManager.getIfAvailable();
		this.transactionManagerCustomizers = transactionManagerCustomizers
				.getIfAvailable();
	}

    // 定义 bean 事务处理器 PlatformTransactionManager transactionManager,
    // 缺省情况下的实现类使用 JpaTransactionManager
	@Bean
    // 仅在该 bean 未被定义的情况下才定义
	@ConditionalOnMissingBean
	public PlatformTransactionManager transactionManager() {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		if (this.transactionManagerCustomizers != null) {
			this.transactionManagerCustomizers.customize(transactionManager);
		}
		return transactionManager;
	}

    // 定义 bean JpaVendorAdapter jpaVendorAdapter, JPA供应商实现适配器
	@Bean
    // 仅在该 bean 未被定义的情况下才定义
	@ConditionalOnMissingBean   
	public JpaVendorAdapter jpaVendorAdapter() {
		AbstractJpaVendorAdapter adapter = createJpaVendorAdapter();
		adapter.setShowSql(this.properties.isShowSql());
		adapter.setDatabase(this.properties.determineDatabase(this.dataSource));
		adapter.setDatabasePlatform(this.properties.getDatabasePlatform());
		adapter.setGenerateDdl(this.properties.isGenerateDdl());
		return adapter;
	}

    // 定义 bean EntityManagerFactoryBuilder entityManagerFactoryBuilder
    // 该 bean 会使用其他地方定义的 JpaVendorAdapter, PersistenceUnitManager
    // 构建一个 EntityManagerFactoryBuilder, 这是一个 EntityManagerFactory 构建器
    // 此 bean 可以理解成底层组件提供者 JpaVendorAdapter, PersistenceUnitManager 跟
    // 上层 LocalContainerEntityManagerFactoryBean 使用者之间的一个纽带
	@Bean
    // 仅在该 bean 未被定义的情况下才定义
	@ConditionalOnMissingBean
	public EntityManagerFactoryBuilder entityManagerFactoryBuilder(
			JpaVendorAdapter jpaVendorAdapter,
			ObjectProvider<PersistenceUnitManager> persistenceUnitManager,
			ObjectProvider<EntityManagerFactoryBuilderCustomizer> customizers) {
		EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(
				jpaVendorAdapter, this.properties.getProperties(),
				persistenceUnitManager.getIfAvailable());
		customizers.orderedStream()
				.forEach((customizer) -> customizer.customize(builder));
		return builder;
	}

    // 定义 bean LocalContainerEntityManagerFactoryBean entityManagerFactory
    // 该 bean 定义方法使用上面定义的 EntityManagerFactoryBuilder bean 执行其构建过程,
    // 产出物为一个 LocalContainerEntityManagerFactoryBean , 这其实是一个
    // EntityManagerFactory, EntityManager 的工厂组件 bean
	@Bean
	@Primary
    // 仅在同类型 bean 和 类型为 EntityManagerFactory 的 bean 未被定义的情况下才定义
	@ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class,
			EntityManagerFactory.class })
	public LocalContainerEntityManagerFactoryBean entityManagerFactory(
			EntityManagerFactoryBuilder factoryBuilder) {
		Map<String, Object> vendorProperties = getVendorProperties();
		customizeVendorProperties(vendorProperties);
		return factoryBuilder.dataSource(this.dataSource).packages(getPackagesToScan())
				.properties(vendorProperties).mappingResources(getMappingResources())
				.jta(isJta()).build();
	}

	protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter();

	protected abstract 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) {
	}

	protected String[] getPackagesToScan() {
		List<String> packages = EntityScanPackages.get(this.beanFactory)
				.getPackageNames();
		if (packages.isEmpty() && AutoConfigurationPackages.has(this.beanFactory)) {
			packages = AutoConfigurationPackages.get(this.beanFactory);
		}
		return StringUtils.toStringArray(packages);
	}

	private String[] getMappingResources() {
		List<String> mappingResources = this.properties.getMappingResources();
		return (!ObjectUtils.isEmpty(mappingResources)
				? StringUtils.toStringArray(mappingResources) : null);
	}

	/**
	 * Return the JTA transaction manager.
	 * @return the transaction manager or null
	 */
	protected JtaTransactionManager getJtaTransactionManager() {
		return this.jtaTransactionManager;
	}

	/**
	 * Returns if a JTA PlatformTransactionManager is being used.
	 * @return if a JTA transaction manager is being used
	 */
	protected final boolean isJta() {
		return (this.jtaTransactionManager != null);
	}

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

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

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
	}


    // 内部配置类, 对 Open Session In View 模式的支持
	@Configuration
	@ConditionalOnWebApplication(type = Type.SERVLET)
	@ConditionalOnClass(WebMvcConfigurer.class)
	@ConditionalOnMissingBean({ OpenEntityManagerInViewInterceptor.class,
			OpenEntityManagerInViewFilter.class })
	@ConditionalOnMissingFilterBean(OpenEntityManagerInViewFilter.class)
	@ConditionalOnProperty(prefix = "spring.jpa", name = "open-in-view", 
		havingValue = "true", matchIfMissing = true)
	protected static class JpaWebConfiguration {

		// Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when
		// not on the classpath
		@Configuration
		protected static class JpaWebMvcConfiguration implements WebMvcConfigurer {

			private static final Log logger = LogFactory
					.getLog(JpaWebMvcConfiguration.class);

			private final JpaProperties jpaProperties;

			protected JpaWebMvcConfiguration(JpaProperties jpaProperties) {
				this.jpaProperties = jpaProperties;
			}

			@Bean
			public OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor() {
				if (this.jpaProperties.getOpenInView() == null) {
					logger.warn("spring.jpa.open-in-view is enabled by default. "
							+ "Therefore, database queries may be performed during view "
							+ "rendering. Explicitly configure "
							+ "spring.jpa.open-in-view to disable this warning");
				}
				return new OpenEntityManagerInViewInterceptor();
			}

			@Override
			public void addInterceptors(InterceptorRegistry registry) {
				registry.addWebRequestInterceptor(openEntityManagerInViewInterceptor());
			}

		}

	}

}

参考文章

<think>我们正在解决两个关键问题: 1. 配置Spring Boot使用HibernateTransactionManager进行事务管理 2. 解决之前出现的ClassCastException(EntityManagerHolder无法转换为SessionHolder) 根据引用[1][2][3],我们了解到: - Spring通过@Transactional注解和AOP代理管理事务 - Hibernate的核心API中Session和Transaction需要正确管理 - 之前出现的ClassCastException通常是由于混合使用JPA(EntityManager)和Hibernate原生API(Session)导致的事务资源持有者类型不匹配 因此,配置HibernateTransactionManager的关键在于: 1. 明确使用Hibernate原生API(SessionFactory)而非JPA的EntityManagerFactory 2. 确保整个应用统一使用Hibernate原生事务管理 以下是详细步骤和代码示例: ### 一、Spring Boot配置HibernateTransactionManager #### 1. 添加依赖(确保排除JPA默认配置) ```xml <dependencies> <!-- 使用Spring Boot Starter Data JPA但排除默认的JPA事务管理器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </exclusion> </exclusions> </dependency> <!-- 添加Hibernate核心依赖 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.6.15.Final</version> <!-- 版本与Spring Boot匹配 --> </dependency> </dependencies> ``` #### 2. 配置数据源和SessionFactory ```java @Configuration @EnableTransactionManagement // 启用事务管理[^2] public class HibernateConfig { @Bean public LocalSessionFactoryBean sessionFactory(DataSource dataSource) { LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setPackagesToScan("com.example.domain"); // 实体类包路径 sessionFactory.setHibernateProperties(hibernateProperties()); return sessionFactory; } private Properties hibernateProperties() { Properties properties = new Properties(); properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect"); properties.put("hibernate.show_sql", true); properties.put("hibernate.format_sql", true); // 关键:设置Hibernate使用JDBC事务(避免JPA介入) properties.put("hibernate.current_session_context_class", "org.springframework.orm.hibernate5.SpringSessionContext"); return properties; } // 配置HibernateTransactionManager[^3] @Bean public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) { HibernateTransactionManager txManager = new HibernateTransactionManager(); txManager.setSessionFactory(sessionFactory); return txManager; } } ``` #### 3. 使用事务 ```java @Service public class UserService { @Autowired private SessionFactory sessionFactory; @Transactional // 使用事务[^2] public void createUser(User user) { Session session = sessionFactory.getCurrentSession(); // 由Spring管理Session session.save(user); // 无需手动begin/commit,由AOP代理自动处理[^1][^2] } } ``` ### 二、解决ClassCastException #### 错误原因 - 同时存在两种事务管理器(JpaTransactionManager和HibernateTransactionManager) - 代码中混用`EntityManager`和`Session`(如:`@PersistenceContext`注入EntityManager后又调用`unwrap(Session.class)`) - Spring Boot自动配置冲突(默认激活JPA配置) #### 解决方案 1. **统一使用Hibernate原生API** - 删除所有`@PersistenceContext`注入 - 改为注入`SessionFactory`并通过`getCurrentSession()`获取Session 2. **禁用JPA自动事务管理** 在启动类排除JPA自动配置: ```java @SpringBootApplication(exclude = { DataSourceTransactionManagerAutoConfiguration.class, // 排除JDBC事务 JpaTransactionManagerConfiguration.class, // 排除JPA事务 HibernateJpaAutoConfiguration.class // 排除Hibernate JPA自动配置 }) public class Application { ... } ``` 3. **检查热部署干扰** 在`application.properties`中临时关闭devtools: ```properties spring.devtools.restart.enabled=false ``` 重启后验证是否解决(类加载器冲突可能导致类型转换异常)[^5] ### 三、常见错误处理 | 错误现象 | 原因 | 解决方案 | |---------|------|---------| | `No Session found for current thread` | 未配置`SpringSessionContext` | 在hibernateProperties中添加:<br>`hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext` | | `get is not valid without active transaction` | 未启用事务管理[^1] | 1. 检查`@EnableTransactionManagement`<br>2. 确保方法有`@Transactional`注解 | | `Bean named 'sessionFactory' must be of type ...` | 依赖冲突 | 检查是否同时存在多个SessionFactory定义<br>排除`spring-boot-starter-data-jpa`中的Hibernate-JPA依赖 | > **关键提示**:Spring Boot默认优先使用JPA配置。若需纯Hibernate方案,必须显式排除JPA自动配置[^4]。 §§相关问题§§ 1. 如何在Spring Boot中同时使用JPA和Hibernate原生API? 2. Spring事务管理器的选择标准是什么? 3. 如何配置多数据源的事务管理? 4. Hibernate的SessionFactory和JPA的EntityManagerFactory有什么区别? 5. Spring Boot中如何自定义事务隔离级别?</think>### Spring Boot 配置 HibernateTransactionManager 事务管理指南 #### 一、核心配置步骤 1. **排除JPA自动配置** 在启动类中禁用JPA默认事务管理器: ```java @SpringBootApplication(exclude = { DataSourceTransactionManagerAutoConfiguration.class, JpaTransactionManagerConfiguration.class }) public class Application { ... } ``` 2. **配置 SessionFactory** ```java @Bean public LocalSessionFactoryBean sessionFactory(DataSource dataSource) { LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setPackagesToScan("com.example.entity"); sessionFactory.setHibernateProperties(hibernateProperties()); return sessionFactory; } private Properties hibernateProperties() { Properties props = new Properties(); props.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect"); props.put("hibernate.show_sql", true); // 关键配置:启用Spring管理的Session上下文[^1] props.put("hibernate.current_session_context_class", "org.springframework.orm.hibernate5.SpringSessionContext"); return props; } ``` 3. **配置 HibernateTransactionManager** ```java @Bean public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) { HibernateTransactionManager txManager = new HibernateTransactionManager(); txManager.setSessionFactory(sessionFactory); return txManager; } ``` 4. **启用事务管理** ```java @Configuration @EnableTransactionManagement // 启用声明式事务[^2] public class TransactionConfig { ... } ``` #### 二、使用事务的正确方式 ```java @Service public class UserService { @Autowired private SessionFactory sessionFactory; @Transactional // 方法级事务注解[^2] public void updateUser(User user) { Session session = sessionFactory.getCurrentSession(); // 自动获取绑定到事务的Session session.update(user); // 无需手动begin/commit,由Spring AOP代理处理[^1][^2] } @Transactional(readOnly = true) public User getUser(Long id) { return sessionFactory.getCurrentSession().get(User.class, id); } } ``` #### 三、解决 ClassCastException 错误 **错误原因**: `EntityManagerHolder cannot be cast to SessionHolder` 表明: 1. 同时激活了JPA和Hibernate两种事务管理器 2. 代码中混用了`EntityManager`和`Session` API 3. Spring事务资源持有者类型冲突[^1] **解决方案**: 1. **统一事务管理器** 确保只使用一种事务管理器(推荐`HibernateTransactionManager`) 2. **禁用JPA资源绑定** ```java @Bean public EntityManagerFactory entityManagerFactory() { return null; // 禁用JPA EntityManager } ``` 3. **代码层统一API** ```java // 错误:混用API @PersistenceContext EntityManager em; @Transactional public void wrongMethod() { Session session = em.unwrap(Session.class); // 导致类型冲突 } // 正确:统一使用SessionFactory @Autowired SessionFactory sessionFactory; ``` #### 四、常见错误及解决 | 错误信息 | 原因 | 解决方案 | |----------|------|----------| | `No Session found for current thread` | 未配置`SpringSessionContext` | 在hibernateProperties中添加:<br>`hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext` | | `get is not valid without active transaction` | 方法未加`@Transactional`[^1] | 在操作数据库的方法添加`@Transactional` | | `BeanCreationException: SessionFactory not found` | 未正确注入SessionFactory | 检查`@Bean SessionFactory`配置 | | `Connection is read-only` | 只读事务中执行写操作 | 修改事务属性:`@Transactional(readOnly = false)` | #### 五、最佳实践 1. **事务边界控制** ```java @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, timeout = 30, rollbackFor = {SQLException.class}) ``` 2. **避免Session泄漏** ```java // 错误:手动管理Session Session session = sessionFactory.openSession(); session.beginTransaction(); // 正确:使用Spring托管 @Transactional public void safeMethod() { sessionFactory.getCurrentSession().save(entity); } ``` 3. **事务监控配置** ```properties # application.properties logging.level.org.springframework.orm.hibernate5=DEBUG logging.level.org.springframework.transaction=TRACE ``` > **关键提示**:当使用`getCurrentSession()`时,必须配置`SpringSessionContext`(步骤1)才能实现事务绑定[^3]。Spring Boot 2.5+ 版本需额外排除`JpaBaseConfiguration`避免自动配置冲突。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值