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:
<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。
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();
}
}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 注解替代显式的事务管理代码。
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);
// ... 业务逻辑
}
}class ProductServiceImpl(private val productDao: ProductDao) : ProductService {
@Transactional
fun increasePriceOfAllProductsInCategory(category: String) {
val productsToChange = productDao.loadProductsByCategory(category)
// ... 业务逻辑
}
}在配置中,你需要定义 HibernateTransactionManager:
<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 支持后台异步启动:
LocalSessionFactoryBean bean = new LocalSessionFactoryBean();
bean.setBootstrapExecutor(new SimpleAsyncTaskExecutor()); // 开启异步启动这样 Hibernate 的初始化会与 Spring 容器的其他 Bean 初始化并行运行,显著缩短大型应用的启动时间。
3. getCurrentSession() 的底层魔法
当你调用 sessionFactory.getCurrentSession() 时,Hibernate 会询问 Spring:“现在有正在运行的事务吗?” Spring 拦截这个请求并返回与当前线程/事务绑定的那个 Session。
- 自动资源管理:事务结束时,Spring 会自动 flush 缓存并关闭这个 Session。
- 线程安全:你不必担心多个线程共享 Session 导致的并发异常,Spring 保证了每个事务的隔离性。