Skip to content

Hibernate

本节介绍 Spring 环境下的 Hibernate 集成,并以此展示 Spring 集成 ORM 映射工具的通用方法。本节将详细讨论 DAO 实现、事务划分等多种模式。

注意

  • Spring Framework 7.0 开始,Spring 的 HibernateJpaVendorAdapter 要求使用 Hibernate ORM 7.x
  • org.springframework.orm.jpa.hibernate 包取代了之前的 orm.hibernate5。它专为 Hibernate ORM 7.1+ 设计,与 HibernateJpaVendorAdapter 紧密集成。

在 Spring 容器中配置 SessionFactory

为了避免应用对象与硬编码的资源查找耦合,你可以将 DataSource 或 Hibernate SessionFactory 定义为 Spring 容器中的 Bean。

以下是一个 XML 配置示例,展示了如何设置 LocalSessionFactoryBean

xml
<beans>
	<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
		<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
		<property name="username" value="sa"/>
		<property name="password" value=""/>
	</bean>

	<bean id="mySessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
		<property name="dataSource" ref="myDataSource"/>
		<property name="mappingResources">
			<list>
				<value>product.hbm.xml</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<value>
				hibernate.dialect=org.hibernate.dialect.HSQLDialect
			</value>
		</property>
	</bean>
</beans>

提示

Spring 还提供了 LocalSessionFactoryBuilder,可以无缝集成到 @Bean 风格的配置和编程化设置中。

基于原生 Hibernate API 实现 DAO

Hibernate 具有“上下文相关会话(Contextual Sessions)”特性,即 Hibernate 自身管理每个事务一个当前的 Session

java
public class ProductDaoImpl implements ProductDao {

	private SessionFactory sessionFactory;

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public Collection loadProductsByCategory(String category) {
		return this.sessionFactory.getCurrentSession()
				.createQuery("from test.Product product where product.category=?")
				.setParameter(0, category)
				.list();
	}
}
kotlin
class ProductDaoImpl(private val sessionFactory: SessionFactory) : ProductDao {

	fun loadProductsByCategory(category: String): Collection<*> {
		return sessionFactory.currentSession
				.createQuery("from test.Product product where product.category=?")
				.setParameter(0, category)
				.list()
	}
}

这种风格的优势在于它仅依赖于 Hibernate API,不需要导入任何 Spring 类,对 Hibernate 开发者非常友好。

声明式事务划分

我们推荐使用 Spring 的声明式事务支持,通过 @Transactional 注解替代显式的事务管理代码。

java
public class ProductServiceImpl implements ProductService {

	private ProductDao productDao;

	public void setProductDao(ProductDao productDao) {
		this.productDao = productDao;
	}

	@Transactional
	public void increasePriceOfAllProductsInCategory(final String category) {
		List productsToChange = this.productDao.loadProductsByCategory(category);
		// ... 业务逻辑
	}
}
kotlin
class ProductServiceImpl(private val productDao: ProductDao) : ProductService {

	@Transactional
	fun increasePriceOfAllProductsInCategory(category: String) {
		val productsToChange = productDao.loadProductsByCategory(category)
		// ... 业务逻辑
	}
}

在配置中,你需要定义 HibernateTransactionManager

xml
<bean id="transactionManager"
		class="org.springframework.orm.jpa.hibernate.HibernateTransactionManager">
	<property name="sessionFactory" ref="sessionFactory"/>
</bean>

<tx:annotation-driven/>

事务管理策略

HibernateTransactionManager 会管理单个 Hibernate SessionFactory 的事务。如果你需要在多个数据库之间进行分布式事务,可以改用 JtaTransactionManager

HibernateTransactionManager 还可以将 Hibernate 的 JDBC 连接导出给普通的 JDBC 代码使用,这意味着你可以在同一个本地事务中混合使用 Hibernate 和 JdbcTemplate

比较容器管理与本地定义的资源

如果你不使用 EJB,建议坚持使用本地 SessionFactory 设置和 Spring 的事务管理器。这种方式不需要容器部署的复杂性,且能提供完善的事务性 JVM 级二级缓存支持以及分布式事务能力。


补充教学

1. Spring 7.x 与 Hibernate 7.x 的“新飞跃”

随着 Spring Framework 7.0 的发布,Hibernate 的集成包路径发生了显著变化。

  • 不再推荐使用 orm.hibernate5:旧包已逐渐淡出,新的核心位于 org.springframework.orm.jpa.hibernate
  • Jakarta EE 11 准备就绪:Hibernate 7.x 全面支持最新的 Jakarta EE API。
  • JPA 与原生 Hibernate 的融合:现在的 LocalSessionFactoryBean 不仅仅能生成原生的 SessionFactory,它还能作为 JPA 的 EntityManagerFactory。这使得你可以在一个项目中自由切换使用原生 API 和标准 API。

2. 背景引导(Background Bootstrapping):提升启动速度

Hibernate 的启动(扫描实体、验证映射)通常非常耗时。Spring 支持后台异步启动

java
LocalSessionFactoryBean bean = new LocalSessionFactoryBean();
bean.setBootstrapExecutor(new SimpleAsyncTaskExecutor()); // 开启异步启动

这样 Hibernate 的初始化会与 Spring 容器的其他 Bean 初始化并行运行,显著缩短大型应用的启动时间。

3. getCurrentSession() 的底层魔法

当你调用 sessionFactory.getCurrentSession() 时,Hibernate 会询问 Spring:“现在有正在运行的事务吗?” Spring 拦截这个请求并返回与当前线程/事务绑定的那个 Session

  • 自动资源管理:事务结束时,Spring 会自动 flush 缓存并关闭这个 Session。
  • 线程安全:你不必担心多个线程共享 Session 导致的并发异常,Spring 保证了每个事务的隔离性。

Based on Spring Framework.