此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.1.10Spring中文文档

此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.1.10Spring中文文档

我们从 Spring 环境中的 Hibernate 5 开始, 用它来演示 Spring 在集成 OR 映射器时所采用的方法。 本节详细介绍了许多问题,并展示了 DAO 的不同变体 实现和事务划分。这些模式中的大多数可以直接 转换为所有其他受支持的 ORM 工具。本章后面的章节 介绍其他 ORM 技术并展示简要示例。Spring中文文档

从 Spring Framework 6.0 开始,Spring 需要 Hibernate ORM 5.5+ 才能用于 Spring 以及原生 Hibernate 设置。 我们建议将 Hibernate ORM 5.6 作为该代 Hibernate 的最后一个功能分支。HibernateJpaVendorAdapterSessionFactorySpring中文文档

Hibernate ORM 6.x 仅支持作为 JPA 提供程序 ()。 不再支持使用软件包进行普通设置。 对于新的开发项目,我们建议使用 Hibernate ORM 6.1/6.2 和 JPA 样式的设置。HibernateJpaVendorAdapterSessionFactoryorm.hibernate5Spring中文文档

从 Spring Framework 6.0 开始,Spring 需要 Hibernate ORM 5.5+ 才能用于 Spring 以及原生 Hibernate 设置。 我们建议将 Hibernate ORM 5.6 作为该代 Hibernate 的最后一个功能分支。HibernateJpaVendorAdapterSessionFactorySpring中文文档

Hibernate ORM 6.x 仅支持作为 JPA 提供程序 ()。 不再支持使用软件包进行普通设置。 对于新的开发项目,我们建议使用 Hibernate ORM 6.1/6.2 和 JPA 样式的设置。HibernateJpaVendorAdapterSessionFactoryorm.hibernate5Spring中文文档

SessionFactory在 Spring 容器中设置

若要避免将应用程序对象绑定到硬编码的资源查找,可以定义 资源(例如 JDBC 或 Hibernate )作为 Bean 中的 弹簧容器。需要访问资源的应用程序对象接收引用 通过 Bean 引用到此类预定义实例,如 DAO 中所示 定义在下一节中。DataSourceSessionFactorySpring中文文档

以下摘录自 XML 应用程序上下文定义,演示如何设置 JDBC 和它之上的 Hibernate:DataSourceSessionFactorySpring中文文档

<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.hibernate5.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>

从本地 Jakarta Commons DBCP 切换到位于 JNDI(通常由应用程序服务器管理)只是一个问题 配置,如以下示例所示:BasicDataSourceDataSourceSpring中文文档

<beans>
	<jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/>
</beans>

您还可以访问位于 JNDI 的位置,使用 Spring 的 / 来检索和公开它。 但是,这在 EJB 上下文之外通常并不常见。SessionFactoryJndiObjectFactoryBean<jee:jndi-lookup>Spring中文文档

Spring 还提供了一个变体,无缝集成 具有样式配置和编程设置(不涉及)。LocalSessionFactoryBuilder@BeanFactoryBeanSpring中文文档

两者和支持背景 引导,Hibernate 初始化与应用程序并行运行 给定 bootstrap 执行程序(例如 . 在 上,这可以通过酒店获得。在编程 上,有一个重载方法,它采用引导执行器参数。LocalSessionFactoryBeanLocalSessionFactoryBuilderSimpleAsyncTaskExecutorLocalSessionFactoryBeanbootstrapExecutorLocalSessionFactoryBuilderbuildSessionFactorySpring中文文档

从 Spring Framework 5.1 开始,这种原生 Hibernate 设置还可以在原生 Hibernate 访问旁边公开用于标准 JPA 交互的 JPA。 有关详细信息,请参阅 JPA 的本机休眠设置EntityManagerFactorySpring中文文档

Spring 还提供了一个变体,无缝集成 具有样式配置和编程设置(不涉及)。LocalSessionFactoryBuilder@BeanFactoryBeanSpring中文文档

两者和支持背景 引导,Hibernate 初始化与应用程序并行运行 给定 bootstrap 执行程序(例如 . 在 上,这可以通过酒店获得。在编程 上,有一个重载方法,它采用引导执行器参数。LocalSessionFactoryBeanLocalSessionFactoryBuilderSimpleAsyncTaskExecutorLocalSessionFactoryBeanbootstrapExecutorLocalSessionFactoryBuilderbuildSessionFactorySpring中文文档

从 Spring Framework 5.1 开始,这种原生 Hibernate 设置还可以在原生 Hibernate 访问旁边公开用于标准 JPA 交互的 JPA。 有关详细信息,请参阅 JPA 的本机休眠设置EntityManagerFactorySpring中文文档

基于普通 Hibernate API 实现 DAO

Hibernate 有一个称为上下文会话的功能,其中 Hibernate 本身管理 每笔交易一个当前。这大致相当于 Spring 的 每个事务同步一个 Hibernate。对应的 DAO 实现类似于以下示例,基于普通的 Hibernate API:SessionSessionSpring中文文档

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 参考文档和示例的风格, 除了持有 In an instance 变量。我们强烈建议 这种基于实例的设置在老式类上 Hibernate 的 CaveatEmptor 示例应用程序。(通常,除非绝对必要,否则不要在变量中保留任何资源。SessionFactorystaticHibernateUtilstaticSpring中文文档

前面的 DAO 示例遵循依赖注入模式。它非常适合 Spring IoC 容器,就像针对 Spring 的 . 您还可以在纯 Java 中设置这样的 DAO(例如,在单元测试中)。为此, 实例化它并使用所需的工厂引用进行调用。作为 Spring Bean 定义,DAO 将类似于以下内容:HibernateTemplatesetSessionFactory(..)Spring中文文档

<beans>

	<bean id="myProductDao" class="product.ProductDaoImpl">
		<property name="sessionFactory" ref="mySessionFactory"/>
	</bean>

</beans>

这种 DAO 风格的主要优点是它仅依赖于 Hibernate API。无导入 任何 Spring 课程都是必需的。这从非侵入性中很有吸引力 对于Hibernate开发人员来说,可能会感觉更自然。Spring中文文档

但是,DAO 抛出 plain(这是未选中的,因此它没有 被声明或捕获),这意味着调用方只能将异常视为 通常是致命的——除非他们想依赖 Hibernate 自己的异常层次结构。 没有,就不可能发现特定原因(例如乐观锁定失败) 将调用方与实施策略联系起来。这种权衡可能是可以接受的 强基于休眠的应用程序不需要任何特殊例外 治疗,或两者兼而有之。HibernateExceptionSpring中文文档

幸运的是,Spring 支持任何 Spring 交易策略的 Hibernate 方法, 返回当前 Spring 管理的事务,即使使用 .该方法的标准行为保持不变 返回与正在进行的 JTA 事务关联的当前数据(如果有)。 无论您使用的是 Spring 的 、EJB 容器托管事务 (CMT) 还是 JTA,此行为都适用。LocalSessionFactoryBeanSessionFactory.getCurrentSession()SessionHibernateTransactionManagerSessionJtaTransactionManagerSpring中文文档

总之,您可以基于普通的 Hibernate API 实现 DAO,同时仍然 能够参与 Spring 管理的交易。Spring中文文档

声明易划分

我们建议你使用 Spring 的声明式事务支持,它允许你 将 Java 代码中的显式事务划分 API 调用替换为 AOP 事务拦截器。您可以在 Spring 中配置此事务拦截器 容器,使用 Java 注解或 XML。此声明性事务功能 使业务服务免于重复的事务分界代码和 专注于添加业务逻辑,这是应用程序的真正价值。Spring中文文档

在您继续之前,我们强烈建议您阅读声明式事务管理(如果您尚未阅读)。

您可以使用注记对服务图层进行注记,并指示 Spring 容器来查找这些注释并为 这些带注释的方法。以下示例演示如何执行此操作:@TransactionalSpring中文文档

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);
		// ...
	}

	@Transactional(readOnly = true)
	public List<Product> findAllProducts() {
		return this.productDao.findAllProducts();
	}
}
class ProductServiceImpl(private val productDao: ProductDao) : ProductService {

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

	@Transactional(readOnly = true)
	fun findAllProducts() = productDao.findAllProducts()
}

在容器中,需要设置实现 (作为 bean)和一个条目,选择在运行时进行处理。以下示例演示如何执行此操作:PlatformTransactionManager<tx:annotation-driven/>@TransactionalSpring中文文档

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/tx
		https://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/aop
		https://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- SessionFactory, DataSource, etc. omitted -->

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

	<tx:annotation-driven/>

	<bean id="myProductService" class="product.SimpleProductService">
		<property name="productDao" ref="myProductDao"/>
	</bean>

</beans>
在您继续之前,我们强烈建议您阅读声明式事务管理(如果您尚未阅读)。

程序化事务划分

您可以在应用程序的更高级别中划分事务,在 跨任意数量操作的较低级别的数据访问服务。限制也没有 存在于周边业务服务的实施上。它只需要一个弹簧.同样,后者可以来自任何地方,但最好是 作为通过方法的 Bean 引用。此外,应通过方法进行设置。以下一对代码片段显示 Spring 应用程序上下文中的事务管理器和业务服务定义 以及业务方法实现的示例:PlatformTransactionManagersetTransactionManager(..)productDAOsetProductDao(..)Spring中文文档

<beans>

	<bean id="myTxManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
		<property name="sessionFactory" ref="mySessionFactory"/>
	</bean>

	<bean id="myProductService" class="product.ProductServiceImpl">
		<property name="transactionManager" ref="myTxManager"/>
		<property name="productDao" ref="myProductDao"/>
	</bean>

</beans>
public class ProductServiceImpl implements ProductService {

	private TransactionTemplate transactionTemplate;
	private ProductDao productDao;

	public void setTransactionManager(PlatformTransactionManager transactionManager) {
		this.transactionTemplate = new TransactionTemplate(transactionManager);
	}

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

	public void increasePriceOfAllProductsInCategory(final String category) {
		this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			public void doInTransactionWithoutResult(TransactionStatus status) {
				List productsToChange = this.productDao.loadProductsByCategory(category);
				// do the price increase...
			}
		});
	}
}
class ProductServiceImpl(transactionManager: PlatformTransactionManager,
						private val productDao: ProductDao) : ProductService {

	private val transactionTemplate = TransactionTemplate(transactionManager)

	fun increasePriceOfAllProductsInCategory(category: String) {
		transactionTemplate.execute {
			val productsToChange = productDao.loadProductsByCategory(category)
			// do the price increase...
		}
	}
}

Spring 允许抛出任何选中的应用程序异常 替换回调代码,while 被限制为未选中 回调中的异常。 在以下情况下触发回滚 未经检查的应用程序异常,或者事务标记为仅回滚 应用程序(通过设置 )。默认情况下,行为方式相同,但允许每个方法可配置的回滚策略。TransactionInterceptorTransactionTemplateTransactionTemplateTransactionStatusTransactionInterceptorSpring中文文档

交易管理策略

两者并委托实际事务 处理到实例(可以是(对于单个 Hibernate )通过使用引擎盖下)或 (委托给 容器的 JTA 子系统)用于 Hibernate 应用程序。您甚至可以使用自定义实现。从本机 Hibernate 事务切换 管理到JTA(例如当面对某些分布式事务要求时 应用程序的部署)只是一个配置问题。您可以替换 具有 Spring 的 JTA 事务实现的 Hibernate 事务管理器。双 事务划分和数据访问代码无需更改即可工作,因为它们 使用通用事务管理 API。TransactionTemplateTransactionInterceptorPlatformTransactionManagerHibernateTransactionManagerSessionFactoryThreadLocalSessionJtaTransactionManagerPlatformTransactionManagerSpring中文文档

对于跨多个 Hibernate 会话工厂的分布式事务,您可以将具有多个定义的事务策略组合在一起。然后,每个 DAO 都会将一个特定的引用传递到其相应的 Bean 属性中。如果所有底层 JDBC 数据 源是事务容器的源,业务服务可以划分事务 跨越任意数量的 DAO 和任意数量的会话工厂,无需特别考虑,如 只要它用作策略。JtaTransactionManagerLocalSessionFactoryBeanSessionFactoryJtaTransactionManagerSpring中文文档

两者都允许适当的 使用 Hibernate 进行 JVM 级高速缓存处理,无需特定于容器的事务管理器 查找或 JCA 连接器(如果不使用 EJB 启动事务)。HibernateTransactionManagerJtaTransactionManagerSpring中文文档

HibernateTransactionManager可以将 Hibernate JDBC 导出为普通 JDBC 特定 .此能力允许高级 使用混合 Hibernate 和 JDBC 数据访问的事务划分,完全没有 JTA,前提是您只访问一个数据库。 自然而然 如果已使用 through 类的属性设置了传入,则将 Hibernate 事务公开为 JDBC 事务。或者,可以显式指定应通过类的属性公开事务的内容。ConnectionDataSourceHibernateTransactionManagerSessionFactoryDataSourcedataSourceLocalSessionFactoryBeanDataSourcedataSourceHibernateTransactionManagerSpring中文文档

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

您可以在容器管理的 JNDI 和本地定义的 JNDI 之间切换 一个,无需更改任何一行应用程序代码。是否保留 容器中或应用程序内部的本地资源定义主要是 您使用的交易策略的问题。与Spring定义的本地相比,手动注册的JNDI不提供任何 好处。通过 Hibernate 的 JCA 连接器部署 参与 Jakarta EE 服务器管理基础设施的附加值,但确实如此 除此之外不会增加实际价值。SessionFactorySessionFactorySessionFactorySessionFactorySpring中文文档

Spring 的事务支持不绑定到容器。配置任何策略时 除了 JTA 之外,事务支持还可以在独立环境或测试环境中工作。 特别是在单数据库事务的典型案例中,Spring 的单一资源 本地事务支持是 JTA 的轻量级且功能强大的替代方案。当您使用 本地 EJB 无状态会话 Bean 来驱动事务,您都依赖于 EJB 容器和 JTA 上,即使您只访问单个数据库并且仅使用无状态数据库 会话 Bean,用于通过容器管理提供声明性事务 交易。以编程方式直接使用 JTA 还需要 Jakarta EE 环境。Spring中文文档

Spring 驱动的事务可以与本地定义的 Hibernate 一起使用,就像使用本地 JDBC 一样,前提是它们访问 单一数据库。因此,您只需要在以下情况下使用 Spring 的 JTA 事务策略: 有分布式事务要求。JCA 连接器需要特定于容器 部署步骤,以及(显然)首先是 JCA 支持。此配置 与使用本地资源部署简单的 Web 应用程序相比,需要更多的工作 定义和 Spring 驱动的事务。SessionFactoryDataSourceSpring中文文档

考虑到所有因素,如果您不使用 EJB,请坚持使用本地设置 和 Spring 的 or .你得到所有 好处,包括适当的事务性 JVM 级缓存和分布式 事务,没有容器部署的不便。JNDI注册 通过 JCA 连接器休眠仅在以下情况下使用时才增加价值 与 EJB 结合使用。SessionFactoryHibernateTransactionManagerJtaTransactionManagerSessionFactorySpring中文文档

Hibernate 的虚假应用程序服务器警告

在一些具有非常严格实现的 JTA 环境中(目前 某些 WebLogic Server 和 WebSphere 版本),当 Hibernate 配置时没有 关于该环境的 JTA 事务管理器、虚假警告或 异常可能会显示在应用程序服务器日志中。这些警告或例外 指示正在访问的连接不再有效或 JDBC 访问为否 有效期更长,可能是因为事务不再处于活动状态。举个例子, 这是 WebLogic 的一个实际例外:XADataSourceSpring中文文档

java.sql.SQLException: The transaction is no longer active - status: 'Committed'. No
further JDBC access is allowed within this transaction.

另一个常见问题是 JTA 事务后使用 Hibernate 的连接泄漏 会话(以及潜在的底层 JDBC 连接)未正确关闭。Spring中文文档

您可以通过让 Hibernate 了解 JTA 事务管理器来解决此类问题, 它与它同步(与 Spring 一起)。执行此操作有两个选项:Spring中文文档

  • 将 Spring Bean 传递给 Hibernate 设置。最简单的 way 是 Bean 属性的 Bean 引用(请参阅 Hibernate 事务设置)。 然后,Spring 将相应的 JTA 策略提供给 Hibernate。JtaTransactionManagerjtaTransactionManagerLocalSessionFactoryBeanSpring中文文档

  • 您还可以显式配置 Hibernate 的 JTA 相关属性,特别是 “hibernate.transaction.coordinator_class”,“hibernate.connection.handling_mode” 以及“hibernateProperties”中的潜在“hibernate.transaction.jta.platform” on(有关这些属性的详细信息,请参阅 Hibernate 的手册)。LocalSessionFactoryBeanSpring中文文档

本节的其余部分描述了与 和 一起发生的事件的顺序 没有 Hibernate 对 JTA 的认识.PlatformTransactionManagerSpring中文文档

当 Hibernate 未配置任何 JTA 事务管理器感知时, JTA 事务提交时发生以下事件:Spring中文文档

  • JTA 事务提交。Spring中文文档

  • Spring 与 JTA 事务同步,因此它是 通过 JTA 事务管理器的回调回调。JtaTransactionManagerafterCompletionSpring中文文档

  • 在其他活动中,此同步可以触发 Spring 的回调到 Hibernate,通过 Hibernate 的回调(用于清除 Hibernate 缓存),然后对 Hibernate 会话进行显式调用, 这会导致 Hibernate 尝试 JDBC 连接。afterTransactionCompletionclose()close()Spring中文文档

  • 在某些环境中,此调用会触发警告或 错误,因为应用程序服务器不再认为 是可用的, 因为交易已经提交。Connection.close()ConnectionSpring中文文档

当 Hibernate 配置了 JTA 事务管理器的感知时, JTA 事务提交时发生以下事件:Spring中文文档

  • JTA 事务已准备好提交。Spring中文文档

  • Spring 与 JTA 事务同步,因此 事务通过 JTA 的回调回调回 事务管理器。JtaTransactionManagerbeforeCompletionSpring中文文档

  • Spring 知道 Hibernate 本身与 JTA 事务同步,并且 行为与上一个方案不同。特别是,它与 Hibernate 的事务性资源管理。Spring中文文档

  • JTA 事务提交。Spring中文文档

  • Hibernate 与 JTA 事务同步,因此事务被回调 通过JTA事务管理器的回调,可以 正确清除其缓存。afterCompletionSpring中文文档