对于最新的稳定版本,请使用 Spring Framework 6.2.0spring-doc.cn

JPA

该软件包下提供的 Spring JPA 提供 对 Java 持久性的全面支持 API 以类似于与 Hibernate 集成的方式进行,同时了解 底层实现,以便提供额外的功能。org.springframework.orm.jpaspring-doc.cn

在 Spring 环境中设置 JPA 的三个选项

Spring JPA 支持提供了三种设置 JPA 的方法,应用程序使用该 JPA 来获取实体管理器。EntityManagerFactoryspring-doc.cn

LocalEntityManagerFactoryBean

您只能在简单的部署环境(如独立部署环境)中使用此选项 应用程序和集成测试。spring-doc.cn

创建适合 应用程序仅使用 JPA 进行数据访问的简单部署环境。 工厂 Bean 使用 JPA 自动检测机制(根据 添加到 JPA 的 Java SE 引导),并且在大多数情况下,要求您仅指定 持久性单元名称。下面的 XML 示例配置了这样的 bean:LocalEntityManagerFactoryBeanEntityManagerFactoryPersistenceProviderspring-doc.cn

<beans>
	<bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="myPersistenceUnit"/>
	</bean>
</beans>

这种形式的 JPA 部署是最简单且最有限的。您不能引用 现有的 JDBC bean 定义,并且不支持全局事务 存在。此外,持久化类的编织(字节码转换)是 特定于提供程序,通常需要在启动时指定特定的 JVM 代理。这 选项仅适用于独立应用程序和测试环境,对于这些应用程序和测试环境 设计了 JPA 规范。DataSourcespring-doc.cn

从 JNDI 获取 EntityManagerFactory

在部署到 Jakarta EE 服务器时,可以使用此选项。查看服务器的文档 了解如何将自定义 JPA 提供程序部署到您的服务器中,从而允许不同的 provider 而不是服务器的默认值。spring-doc.cn

从 JNDI 获取 (例如在 Jakarta EE 环境中), 是更改 XML 配置的问题,如下例所示:EntityManagerFactoryspring-doc.cn

<beans>
	<jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/>
</beans>

此操作假定标准 Jakarta EE 引导。Jakarta EE 服务器自动检测 持久化单元(实际上是应用程序 jar 中的文件)和 Jakarta EE 部署描述符中的条目(例如,),并定义这些持久化单元的环境命名上下文位置。META-INF/persistence.xmlpersistence-unit-refweb.xmlspring-doc.cn

在这种情况下,整个持久化单元部署,包括编织 (字节码转换)的持久化类,则取决于 Jakarta EE 服务器。JDBC 是通过文件中的 JNDI 位置定义的。 事务与服务器的 JTA 子系统集成。仅 Spring 使用获取的 ,通过 持久性单元的依赖关系注入和管理事务(通常 通过 )。DataSourceMETA-INF/persistence.xmlEntityManagerEntityManagerFactoryJtaTransactionManagerspring-doc.cn

如果在同一应用程序中使用多个持久性单元,则此类 JNDI 检索到的持久性单元应与 application 用于引用它们(例如,IN 和 Annotations)。@PersistenceUnit@PersistenceContextspring-doc.cn

LocalContainerEntityManagerFactoryBean

您可以在基于 Spring 的应用程序环境中使用此选项来获得完整的 JPA 功能。 这包括 Web 容器(如 Tomcat)、独立应用程序以及 具有复杂持久性要求的集成测试。spring-doc.cn

可以完全控制配置,适用于满足以下条件的环境 需要精细的自定义。将基于文件创建一个实例, 提供的策略和指定的 .因此, 可以在 JNDI 之外使用自定义数据源并控制编织 过程。以下示例显示了 a 的典型 bean 定义:LocalContainerEntityManagerFactoryBeanEntityManagerFactoryLocalContainerEntityManagerFactoryBeanPersistenceUnitInfopersistence.xmldataSourceLookuploadTimeWeaverLocalContainerEntityManagerFactoryBeanspring-doc.cn

<beans>
	<bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="someDataSource"/>
		<property name="loadTimeWeaver">
			<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
		</property>
	</bean>
</beans>

以下示例显示了一个典型文件:persistence.xmlspring-doc.cn

<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
	<persistence-unit name="myUnit" transaction-type="RESOURCE_LOCAL">
		<mapping-file>META-INF/orm.xml</mapping-file>
		<exclude-unlisted-classes/>
	</persistence-unit>
</persistence>
该快捷方式表示不扫描 带注释的实体类应该出现。显式的 'true' 值 () 也表示不扫描。 会触发扫描。 但是,我们建议省略元素 如果您希望进行实体类扫描。<exclude-unlisted-classes/><exclude-unlisted-classes>true</exclude-unlisted-classes/><exclude-unlisted-classes>false</exclude-unlisted-classes/>exclude-unlisted-classes

使用 是最强大的 JPA 设置 选项,允许在应用程序内进行灵活的本地配置。它支持 链接到现有 JDBC ,支持本地和全局事务,并且 等等。但是,它也对运行时环境提出了要求,例如 如果持久性提供程序需要,则提供支持 Weaving 的类加载器的可用性 字节码转换。LocalContainerEntityManagerFactoryBeanDataSourcespring-doc.cn

此选项可能与 Jakarta EE 服务器的内置 JPA 功能冲突。在 完整的 Jakarta EE 环境,请考虑从 JNDI 获取您的环境。 或者,在定义中指定自定义(例如 META-INF/my-persistence.xml),并在 应用程序 jar 文件。由于 Jakarta EE 服务器仅查找默认文件,因此它会忽略此类自定义持久性单元,因此, 避免与 Spring 驱动的 JPA 预先设置冲突。(这适用于 Resin 3.1,用于 示例。EntityManagerFactorypersistenceXmlLocationLocalContainerEntityManagerFactoryBeanMETA-INF/persistence.xmlspring-doc.cn

什么时候需要加载时编织?

并非所有 JPA 提供程序都需要 JVM 代理。Hibernate 是一个不这样做的例子。 如果您的提供商不需要代理,或者您有其他选择,例如 在构建时通过自定义编译器或 Ant 任务应用增强功能时,不应使用 load-time weaver 的 Weaver 中。spring-doc.cn

该接口是 Spring 提供的类,它允许以特定方式插入 JPA 实例,具体取决于 environment 是 Web 容器或应用程序服务器。通过代理程序进行挂钩通常效率不高。代理针对整个虚拟机工作,并且 检查加载的每个类,这在生产中通常是不可取的 服务器环境。LoadTimeWeaverClassTransformerClassTransformersspring-doc.cn

Spring 为各种环境提供了许多实现, 让实例仅应用于每个类加载器,而不是 对于每个 VM。LoadTimeWeaverClassTransformerspring-doc.cn

请参阅 AOP 章节中的 Spring 配置 有关实施及其设置的更多见解 通用或针对各种平台(如 Tomcat、JBoss 和 WebSphere)进行定制。LoadTimeWeaverspring-doc.cn

Spring 配置中所述,您可以配置 使用 annotation 或 XML 元素进行上下文范围的调用。这样的全局 weaver 会自动被选中 按所有 JPA 实例。以下示例 显示设置加载时编织机的首选方法,提供自动检测 平台(例如 Tomcat 的具有 weaving 功能的类加载器或 Spring 的 JVM 代理) 以及将 Weaver 自动传播到所有可识别 Weaver 的 bean:LoadTimeWeaver@EnableLoadTimeWeavingcontext:load-time-weaverLocalContainerEntityManagerFactoryBeanspring-doc.cn

<context:load-time-weaver/>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
	...
</bean>

但是,如果需要,您可以通过该属性手动指定专用编织器,如下例所示:loadTimeWeaverspring-doc.cn

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
	<property name="loadTimeWeaver">
		<bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
	</property>
</bean>

无论 LTW 是如何配置的,通过使用这种技术,依赖于 插桩可以在目标平台(例如 Tomcat)中运行,而无需代理。 当托管应用程序依赖于不同的 JPA 时,这一点尤其重要 实现,因为 JPA 转换器只应用于类加载器级别,而 因此,彼此隔离。spring-doc.cn

处理多个持久性单元

对于依赖多个持久化单元位置(存储在各种 JARS),Spring 提供了 to 作为 中央存储库,并避免 Persistence Units 发现过程,该过程可以是 贵。默认实现允许指定多个位置。这些位置是 解析,然后通过 Persistence Unit Name 检索。(默认情况下,类路径 将搜索文件。以下示例配置 多个地点:PersistenceUnitManagerMETA-INF/persistence.xmlspring-doc.cn

<bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
	<property name="persistenceXmlLocations">
		<list>
			<value>org/springframework/orm/jpa/domain/persistence-multi.xml</value>
			<value>classpath:/my/package/**/custom-persistence.xml</value>
			<value>classpath*:META-INF/persistence.xml</value>
		</list>
	</property>
	<property name="dataSources">
		<map>
			<entry key="localDataSource" value-ref="local-db"/>
			<entry key="remoteDataSource" value-ref="remote-db"/>
		</map>
	</property>
	<!-- if no datasource is specified, use this one -->
	<property name="defaultDataSource" ref="remoteDataSource"/>
</bean>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
	<property name="persistenceUnitManager" ref="pum"/>
	<property name="persistenceUnitName" value="myCustomUnit"/>
</bean>

默认实现允许自定义实例 (在它们被提供给 JPA 提供程序之前)要么声明地(通过其属性,要么 影响所有托管单位)或编程方式(通过 ,允许选择持久性单位)。如果未指定 no,则创建一个 .PersistenceUnitInfoPersistenceUnitPostProcessorPersistenceUnitManagerLocalContainerEntityManagerFactoryBeanspring-doc.cn

后台引导

LocalContainerEntityManagerFactoryBean支持后台引导 该属性,如下例所示:bootstrapExecutorspring-doc.cn

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
	<property name="bootstrapExecutor">
		<bean class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>
	</property>
</bean>

实际的 JPA 提供程序引导将移交给指定的执行程序,然后 并行运行,以执行应用程序引导线程。公开的代理可以注入到其他应用程序组件中,甚至能够响应配置检查。但是,一旦实际的 JPA 提供程序 正在被其他组件访问(例如,调用 ),这些 calls 会阻塞,直到后台引导完成。特别是,当您使用 Spring Data JPA,请确保还为其存储库设置延迟引导。EntityManagerFactoryEntityManagerFactoryInfocreateEntityManagerspring-doc.cn

基于 JPA 实现 DAO:和EntityManagerFactoryEntityManager

尽管实例是线程安全的,但实例 不是。注入的 JPA 的行为类似于从 应用程序服务器的 JNDI 环境,如 JPA 规范所定义。它委派 对当前 transactional 的所有调用(如果有)。否则,它会回退 添加到新创建的 per 操作中,实际上使其使用线程安全。EntityManagerFactoryEntityManagerEntityManagerEntityManagerEntityManagerEntityManager

可以针对普通 JPA 编写代码,而没有任何 Spring 依赖项,方法是 使用注入的 或 .Spring 可以在字段和方法中理解 and 注解 level (如果启用了 a)。以下示例 显示了使用注解的普通 JPA DAO 实现:EntityManagerFactoryEntityManager@PersistenceUnit@PersistenceContextPersistenceAnnotationBeanPostProcessor@PersistenceUnitspring-doc.cn

public class ProductDaoImpl implements ProductDao {

	private EntityManagerFactory emf;

	@PersistenceUnit
	public void setEntityManagerFactory(EntityManagerFactory emf) {
		this.emf = emf;
	}

	public Collection loadProductsByCategory(String category) {
		EntityManager em = this.emf.createEntityManager();
		try {
			Query query = em.createQuery("from Product as p where p.category = ?1");
			query.setParameter(1, category);
			return query.getResultList();
		}
		finally {
			if (em != null) {
				em.close();
			}
		}
	}
}
class ProductDaoImpl : ProductDao {

	private lateinit var emf: EntityManagerFactory

	@PersistenceUnit
	fun setEntityManagerFactory(emf: EntityManagerFactory) {
		this.emf = emf
	}

	fun loadProductsByCategory(category: String): Collection<*> {
		val em = this.emf.createEntityManager()
		val query = em.createQuery("from Product as p where p.category = ?1");
		query.setParameter(1, category);
		return query.resultList;
	}
}

前面的 DAO 不依赖于 Spring,并且仍然非常适合 Spring 应用程序上下文。此外,DAO 利用注解来要求 注入默认的 ,如下例 bean 定义所示:EntityManagerFactoryspring-doc.cn

<beans>

	<!-- bean post-processor for JPA annotations -->
	<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

	<bean id="myProductDao" class="product.ProductDaoImpl"/>

</beans>

作为显式定义 的替代方法 , 考虑在应用程序中使用 Spring XML 元素 context 配置。这样做会自动注册所有 Spring 标准 后处理器 (post-processors) 用于基于注释的配置,包括 等。PersistenceAnnotationBeanPostProcessorcontext:annotation-configCommonAnnotationBeanPostProcessorspring-doc.cn

请考虑以下示例:spring-doc.cn

<beans>

	<!-- post-processors for all standard config annotations -->
	<context:annotation-config/>

	<bean id="myProductDao" class="product.ProductDaoImpl"/>

</beans>

这种 DAO 的主要问题是它总是创建一个新的 工厂。您可以通过请求事务性(也称为 “shared EntityManager”,因为它是实际事务的共享线程安全代理 EntityManager) 而不是工厂。以下示例显示了如何执行此操作:EntityManagerEntityManagerspring-doc.cn

public class ProductDaoImpl implements ProductDao {

	@PersistenceContext
	private EntityManager em;

	public Collection loadProductsByCategory(String category) {
		Query query = em.createQuery("from Product as p where p.category = :category");
		query.setParameter("category", category);
		return query.getResultList();
	}
}
class ProductDaoImpl : ProductDao {

	@PersistenceContext
	private lateinit var em: EntityManager

	fun loadProductsByCategory(category: String): Collection<*> {
		val query = em.createQuery("from Product as p where p.category = :category")
		query.setParameter("category", category)
		return query.resultList
	}
}

该注释具有一个名为 的可选属性,该属性默认为 自。您可以使用此默认值来接收共享代理。替代项 , 是完全的 不同的事件。这导致了所谓的扩展 ,它不是 线程安全的,因此,不得在并发访问的组件中使用,例如 Spring 管理的单例 bean。只应该使用扩展实例 在有状态组件中,例如,驻留在会话中,其生命周期不与当前事务绑定,而是完全取决于 应用。@PersistenceContexttypePersistenceContextType.TRANSACTIONEntityManagerPersistenceContextType.EXTENDEDEntityManagerEntityManagerEntityManagerspring-doc.cn

方法级和字段级注入

您可以应用注释来指示类中的字段或方法的依赖关系注入(例如 和 ),因此有了表达式 “方法级注入”和“字段级注入”。字段级注释是 简洁易用,而方法级注解允许进一步处理 injected 依赖项。在这两种情况下,成员可见性(公共、受保护或私有) 无关紧要。@PersistenceUnit@PersistenceContextspring-doc.cn

类级注释呢?spring-doc.cn

在 Jakarta EE 平台上,它们用于依赖项声明,而不是资源。 注射。spring-doc.cn

注入的 is 由 Spring 管理的(知道正在进行的事务)。 即使新的 DAO 实现使用方法级注入 an 而不是 an ,也不需要更改 bean 定义 由于 Comments 的使用。EntityManagerEntityManagerEntityManagerFactoryspring-doc.cn

这种 DAO 风格的主要优点是它仅依赖于 Java 持久性 API。 不需要导入任何 Spring 类。此外,正如 JPA 注释所理解的那样, 注入由 Spring 容器自动应用。这很吸引人 非侵入性视角,对于 JPA 开发人员来说可能感觉更自然。spring-doc.cn

基于实现 DAO(通常使用基于构造函数的注入)@Autowired

@PersistenceUnit并且只能在方法和字段上声明。 通过构造函数和其他注入点提供 JPA 资源怎么样?@PersistenceContext@Autowiredspring-doc.cn

EntityManagerFactory可以通过构造函数和字段/方法轻松注入 只要将目标定义为 bean,例如 via . 注入点按类型按原样匹配原始定义。@AutowiredLocalContainerEntityManagerFactoryBeanEntityManagerFactoryspring-doc.cn

但是,-style 共享引用不适用于 开箱即用的常规依赖项注入。为了使其可用于基于类型的 根据需要进行匹配,考虑将 a 定义为 companion 的定义:@PersistenceContextEntityManager@AutowiredSharedEntityManagerBeanEntityManagerFactoryspring-doc.cn

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
	...
</bean>

<bean id="em" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
	<property name="entityManagerFactory" ref="emf"/>
</bean>

或者,您可以根据 :@BeanSharedEntityManagerCreatorspring-doc.cn

@Bean("em")
public static EntityManager sharedEntityManager(EntityManagerFactory emf) {
	return SharedEntityManagerCreator.createSharedEntityManager(emf);
}

如果有多个持久化单元,则每个定义都需要 伴随着相应的 bean 定义,最好带有限定符 与 distinct 定义匹配以区分 持久性单元通过 .EntityManagerFactoryEntityManagerEntityManagerFactory@Autowired @Qualifier("…​")spring-doc.cn

Spring 驱动的 JPA 事务

我们强烈建议您阅读声明式事务管理, 如果您还没有这样做,请更详细地了解 Spring 的声明式事务支持。

JPA 的推荐策略是通过 JPA 的本机事务进行本地事务处理 支持。Spring 提供了许多已知于本地 JDBC 事务(例如特定于事务的隔离级别和资源级别的 read-only optimizations) 针对任何常规 JDBC 连接池(无 XA 要求)。JpaTransactionManagerspring-doc.cn

Spring JPA 还允许配置的 JPA 事务 添加到访问相同 的 JDBC 访问代码中,前提是已注册支持检索底层 JDBC 。 Spring 为 EclipseLink 和 Hibernate JPA 实现提供了方言。 有关机制的详细信息,请参阅下一节JpaTransactionManagerDataSourceJpaDialectConnectionJpaDialectspring-doc.cn

理解和JpaDialectJpaVendorAdapter

作为高级功能, 和 子类 允许将自定义传递到 bean 属性中。实施可以启用以下高级 Spring 支持的功能,通常以特定于供应商的方式:JpaTransactionManagerAbstractEntityManagerFactoryBeanJpaDialectjpaDialectJpaDialectspring-doc.cn

  • 应用特定的事务语义(例如自定义隔离级别或事务 超时)spring-doc.cn

  • 检索事务性 JDBC(用于向基于 JDBC 的 DAO 公开)Connectionspring-doc.cn

  • 到 Spring 的高级翻译PersistenceExceptionDataAccessExceptionspring-doc.cn

这对于特殊事务语义和高级 exception 的翻译。默认实现 () 会 不提供任何特殊功能,并且如果需要前面列出的功能,则您具有 以指定适当的方言。DefaultJpaDialectspring-doc.cn

作为一个更广泛的提供程序适配工具,主要针对 Spring 的全功能设置,它结合了 功能。指定 or 是最方便的 为 Hibernate 或 EclipseLink 自动配置设置的方式, 分别。请注意,这些提供程序适配器主要设计用于 Spring 驱动的事务管理(即,用于)。LocalContainerEntityManagerFactoryBeanJpaVendorAdapterJpaDialectHibernateJpaVendorAdapterEclipseLinkJpaVendorAdapterEntityManagerFactoryJpaTransactionManager

请参阅 JpaDialectJpaVendorAdapter javadoc 以获取 有关其操作的更多详细信息以及如何在 Spring 的 JPA 支持中使用它们。spring-doc.cn

使用 JTA Transaction Management 设置 JPA

作为 的替代方法,Spring 还允许使用多资源 通过 JTA 进行事务协调,无论是在 Jakarta EE 环境中还是使用 独立的事务协调器,例如 Atomikos。除了选择 Spring's 而不是 之外,您还需要更进一步 步骤:JpaTransactionManagerJtaTransactionManagerJpaTransactionManagerspring-doc.cn

  • 底层 JDBC 连接池需要支持 XA,并与 您的交易协调器。这在 Jakarta EE 环境中通常很简单。 通过 JNDI 公开不同类型的 。查看您的应用程序服务器 documentation 了解详细信息。类似地,独立的事务协调器通常 带有特殊的 XA 集成变体。同样,请查看其文档。DataSourceDataSourcespring-doc.cn

  • 需要为 JTA 配置 JPA 设置。这是 特定于提供程序,通常通过特殊属性指定为 on 。在 Hibernate 的情况下,这些属性 甚至特定于版本。有关详细信息,请参阅 Hibernate 文档。EntityManagerFactoryjpaPropertiesLocalContainerEntityManagerFactoryBeanspring-doc.cn

  • Spring 强制执行某些面向 Spring 的默认值,例如 作为连接释放模式,它与 Hibernate 自己在 Hibernate 5.0 版本,但在 Hibernate 5.1+ 中不再运行。对于 JTA 设置,请确保声明 您的 Persistence Unit 事务类型为 “JTA”。或者,将 Hibernate 5.2 的属性设置为以恢复 Hibernate 自己的默认值。 有关相关说明,请参阅 Spurious Application Server Warnings with HibernateHibernateJpaVendorAdapteron-closehibernate.connection.handling_modeDELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENTspring-doc.cn

  • 或者,考虑从您的应用程序中获取 server 本身(即,通过 JNDI 查找而不是 local declaration )。server-provided 可能需要在服务器配置中进行特殊定义(进行部署 不太可移植),但针对服务器的 JTA 环境进行了设置。EntityManagerFactoryLocalContainerEntityManagerFactoryBeanEntityManagerFactoryspring-doc.cn

用于 JPA 交互的本机 Hibernate 设置和本机 Hibernate 事务

本机设置与 允许与其他 JPA 访问代码进行交互。Hibernate 现在原生实现了 JPA 的接口 Hibernate 句柄本身是 JPA 。 Spring 的 JPA 支持工具会自动检测本机 Hibernate 会话。LocalSessionFactoryBeanHibernateTransactionManager@PersistenceContextSessionFactoryEntityManagerFactorySessionEntityManagerspring-doc.cn

因此,这种原生 Hibernate 设置可以替代标准 JPA 和组合 在许多情况下,允许与 (以及 ) 在 Within 之间进行交互 相同的本地事务。这样的设置还提供了更强的 Hibernate 集成 以及更大的配置灵活性,因为它不受 JPA 引导契约的约束。LocalContainerEntityManagerFactoryBeanJpaTransactionManagerSessionFactory.getCurrentSession()HibernateTemplate@PersistenceContext EntityManagerspring-doc.cn

在这种情况下,您不需要配置, 因为 Spring 的原生 Hibernate 设置提供了更多功能 (例如,自定义 Hibernate Integrator 设置、Hibernate 5.3 Bean 容器集成、 以及对只读事务的更强优化)。最后但并非最不重要的一点是,您还可以 express native Hibernate 设置,通过 , 与 style configuration 无缝集成(不涉及)。HibernateJpaVendorAdapterLocalSessionFactoryBuilder@BeanFactoryBeanspring-doc.cn

LocalSessionFactoryBean和支持背景 引导,就像 JPA 一样。 有关介绍,请参阅 Background BootstrappingLocalSessionFactoryBuilderLocalContainerEntityManagerFactoryBeanspring-doc.cn

在 上,这可以通过属性获得。在 programmatic 上,重载方法采用 bootstrap executor 参数。LocalSessionFactoryBeanbootstrapExecutorLocalSessionFactoryBuilderbuildSessionFactoryspring-doc.cn